0. Setup

library(psych)
library(ggplot2)
G3;
Adjuntando el paquete: ‘ggplot2’

gG3;The following objects are masked from ‘package:psych’:

    %+%, alpha

g
library(readxl)#to see the database
library(openxlsx)#to save the database in excel
library(dplyr) # for data manipulation
G3;
Adjuntando el paquete: ‘dplyr’

gG3;The following objects are masked from ‘package:stats’:

    filter, lag

gG3;The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

g
library(tidyr) # for transforming datasets to long and wide formats
library(ordinal) # for ordinal logistic regression
G3;
Adjuntando el paquete: ‘ordinal’

gG3;The following object is masked from ‘package:dplyr’:

    slice

g
library(car) # for VIF calculations
G3;Cargando paquete requerido: carData
gG3;
Adjuntando el paquete: ‘car’

gG3;The following object is masked from ‘package:dplyr’:

    recode

gG3;The following object is masked from ‘package:psych’:

    logit

g
library(vcd) # for visualizing relations between factors
G2;H2;Avisoh: package ‘vcd’ was built under R version 4.5.1g
G3;Cargando paquete requerido: grid
g
library(VGAM)
G2;H2;Avisoh: package ‘VGAM’ was built under R version 4.5.1g
G3;Cargando paquete requerido: stats4
gG3;Cargando paquete requerido: splines
gG3;
Adjuntando el paquete: ‘VGAM’

gG3;The following object is masked from ‘package:car’:

    logit

gG3;The following objects are masked from ‘package:ordinal’:

    dgumbel, dlgamma, pgumbel, plgamma, qgumbel, rgumbel, wine

gG3;The following objects are masked from ‘package:psych’:

    fisherz, logistic, logit

g
library(coefplot) # for plots about coefficients
G2;H2;Avisoh: package ‘coefplot’ was built under R version 4.5.1g

1. Data Simulation

1.1. Simulation of demographic and contextual variables

Variables <- c(“gender”, “age”, “method”, “country”, “reason”, “nps_score”, “satisfaction”, “complain.connect”, “complain.respond.vel”, “complain.solve.vel”, “complain.repeat” )


set.seed(1000)

data.n <- 5*4*2*1000

data <- data.frame (id = as.factor(c(1:data.n)))


data$age <- round(rlnorm(n=data.n, meanlog= log(40), sdlog=log(1.4)))
data$age [data$age>90] <- 90
data$age [data$age<15] <- 15

data$gender <- factor(sample(c("man", "woman"), prob= c(0.5, 0.5), replace= TRUE, size = data.n))
data$country <- factor(sample(c("Drinkland","Eatland"), prob= c(0.5, 0.5), replace= TRUE, size = data.n))
data$method <- factor(sample(c("chat", "web_form", "email", "phone"), prob= c(0.4, 0.10,0.15, 0.35), replace= TRUE, size = data.n))
data$reason <- factor(sample(c("incorrect_item", "delay", "status","cancellation", "other"), prob= c(0.2, 0.3,0.2,0.1, 0.2), replace= TRUE, size = data.n))
data$open.comment <- rep("bla bla", data.n)

Simulation of frequency of complaints depending of the demographic and contextual factors

We will simulate the presence of complaints about the connection (complain.connect), the speed to respond (complain.sp.respond), the speed to solve the problem (complain.sp.solve) and complains about repeating information (complain.repeat), attending to several factors:

  • complaints about connection will be higher in the country Drinkland
  • complaints about speed to response will strongly higher in email, and moderately higher in chat and web forms.
  • complaints about speed to solve will be higher in incorrect item deliveries attended through chat.
  • complaints about the need to repeat information will be higher in order cancellation.
  • other complaints have similar distribution across countries, contact reasons and contact methods

For each type of complaint we will set an initial probability, then we will alter that probability depending on the presence of the mentioned factors.


##For connection complaints
# we set an initial probability of 0.1 of the complaints to appear
con.prob <- rep(0.1,data.n) 
# we set a double probability of connection complains in Drinkland
con.prob [data$country=="Drinkland"] <- con.prob [data$country=="Drinkland"] *2 
#We ensure the probabilities are contained between 0 and 1 (for prevention in further transformations)
con.prob[con.prob>1] <- 1
con.prob[con.prob<0] <- 0
#we create the variable attending the above probabilities for each of our cases
data$complain.connect<- rbinom(n=data.n, size =1, prob=con.prob)
#to check the results are as expected: 
with(data,prop.table(table(complain.connect,country), margin=2))
                country
complain.connect  Drinkland    Eatland
               0 0.79877380 0.90009028
               1 0.20122620 0.09990972
##For complaints about the speed to respond
# we set an initial probability of 0.05 of the complaints to appear
spr.prob <- rep(0.05,data.n) 
# we set a 6 times higher probability for complains to appear for email
spr.prob [data$method=="email"] <- spr.prob [data$method=="email"] *6 
#We set a 3 times higher probability for complaints to appear for chat and web forms
spr.prob [data$method %in% c("chat", "web_form")] <- spr.prob [data$method %in% c("chat", "web_form")] *3
#We ensure the probabilities are contained between 0 and 1 (for prevention in further transformations)
spr.prob[spr.prob>1] <- 1
spr.prob[spr.prob<0] <- 0
#we create the variable attending the above probabilities for each of our cases
data$complain.sp.respond<- rbinom(n=data.n, size =1, prob=spr.prob)
#to check the results are as expected: 
with(data,prop.table(table(complain.sp.respond,method),margin=2))
                   method
complain.sp.respond      chat     email     phone  web_form
                  0 0.8513143 0.6948590 0.9489453 0.8577970
                  1 0.1486857 0.3051410 0.0510547 0.1422030
##For complaints about the speed to solve
# we set an initial probability of 0.1 of the complaints to appear
sps.prob <- rep(0.1,data.n) 
# we set a 5 times higher probability for contact reasons of incorrect items that are attended by chat
sps.prob [data$method=="chat" & data$reason=="incorrect_item"] <- sps.prob [data$method=="chat" & data$reason=="incorrect_item"] *5
#We ensure the probabilities are contained between 0 and 1 (for prevention in further transformations)
sps.prob[sps.prob>1] <- 1
sps.prob[sps.prob<0] <- 0
#we create the variable attending the above probabilities for each of our cases
data$complain.sp.solve<- rbinom(n=data.n, size =1, prob=sps.prob)
#to check the results are as expected: 
with(data,prop.table(table(complain.sp.solve,reason,method),margin=c(2:3)))
, , method = chat

                 reason
complain.sp.solve cancellation      delay incorrect_item      other     status
                0   0.89769976 0.90130873     0.48367030 0.89497857 0.89202454
                1   0.10230024 0.09869127     0.51632970 0.10502143 0.10797546

, , method = email

                 reason
complain.sp.solve cancellation      delay incorrect_item      other     status
                0   0.90252101 0.89886364     0.89542484 0.91181364 0.90712570
                1   0.09747899 0.10113636     0.10457516 0.08818636 0.09287430

, , method = phone

                 reason
complain.sp.solve cancellation      delay incorrect_item      other     status
                0   0.89528433 0.90653753     0.89816481 0.89511238 0.90674673
                1   0.10471567 0.09346247     0.10183519 0.10488762 0.09325327

, , method = web_form

                 reason
complain.sp.solve cancellation      delay incorrect_item      other     status
                0   0.91361257 0.89368771     0.90847914 0.88372093 0.88280255
                1   0.08638743 0.10631229     0.09152086 0.11627907 0.11719745
##For complaints about the need to repeat information
# we set an initial probability of 0.02 of the complaints to appear
rp.prob <- rep(0.02,data.n) 
# we set a 10 times higher probability for contact reasons about cancellations
rp.prob [data$reason=="cancellation"] <- rp.prob [data$reason=="cancellation"] *10
#We ensure the probabilities are contained between 0 and 1 (for prevention in further transformations)
rp.prob[rp.prob>1] <- 1
rp.prob[rp.prob<0] <- 0
#we create the variable attending the above probabilities for each of our cases
data$complain.repeat<- rbinom(n=data.n, size =1, prob=rp.prob)
#to check the results are as expected: 
with(data,prop.table(table(complain.repeat,reason),margin=2))
               reason
complain.repeat cancellation      delay incorrect_item      other     status
              0   0.81061164 0.97864738     0.97990202 0.98145401 0.98006154
              1   0.18938836 0.02135262     0.02009798 0.01854599 0.01993846
##For other complaints

data$complain.other<- rbinom(n=data.n, size =1, prob=.2)
#to check the results are as expected: 
with(data,prop.table(table(complain.other)))
complain.other
      0       1 
0.79655 0.20345 

##Simulation of NPS scores

We first assume a normal distribution of responses in the ideal case in which customers have no complaints.
Then we apply the corrections based on the information we have simulated about the presence of different complaints.

At the end, we limit the scores to 0 and 10 and transform them to integers, in line with restrictions of NPS scores.


#Preliminary distribution in case of no complaints. We use a high mean value
data$nps.score <- rnorm(data.n, mean=9, sd= 2)


#We adjust the scores for the presence of complaints. We are applying fixed penalties to NPS scores based on the presence of complaints (e.g., 3 points if there are connection complaints).While using a binomial distribution could add randomness (and realism), we opt for fixed values to make the relationships easier to interpret. 
data$nps.score <- data$nps.score -
3 * data$complain.connect -
4 * data$complain.sp.respond -
6 * data$complain.sp.solve -
2 * data$complain.repeat -
1 * data$complain.other 

# to limit the values between 0 and 10:
data$nps.score[data$nps.score<0] <- 0
data$nps.score[data$nps.score>10] <- 10 

# to convert to integer values
data$nps.score <- floor (data$nps.score)


#to see the distribution of scores

barplot(table(data$nps.score)) 


with(data,(prop.table(table(data$nps.score))))

       0        1        2        3        4        5        6        7        8        9       10 
0.062575 0.030050 0.042075 0.053425 0.067700 0.084100 0.106525 0.124975 0.131250 0.120450 0.176875 

Specifying the NPS segments based on NPS scores

data$nps.segment <- rep(NA_character_, data.n)

data$nps.segment [data$nps.score<=6] <- "detractor"
data$nps.segment [data$nps.score >6 & data$nps.score <9] <- "passive"
data$nps.segment [data$nps.score>=9] <- "promoter"

data$nps.segment <- factor (data$nps.segment)

#to check the results
with(data, prop.table(table(nps.segment)))
nps.segment
detractor   passive  promoter 
 0.446450  0.256225  0.297325 

##Simulation of satisfaction scores


data$satisfaction <- floor(rnorm(data.n, mean = 3, sd=0.5) + 0.9 * (1 + as.numeric(scale(data$nps.score))))

data$satisfaction[data$satisfaction<1] <- 1
data$satisfaction[data$satisfaction>5] <- 5

# To see the distribution
barplot(table(data$satisfaction))

with(data,prop.table(table(satisfaction)))
satisfaction
      1       2       3       4       5 
0.04970 0.15285 0.28840 0.36965 0.13940 
# To check the correlation with NPS
with(data, cor(nps.score,satisfaction, method="spearman"))
[1] 0.8293446

##Simulation of missing data

###Missing data for satisfaction

We simulate that satisfaction was not available for the country Drinkland, and for the contact methods of email and webform. Additionally, within those situations in which the satisfaction item was available, there is 10% probability that customers do not answer to it.


data$satisfaction[data$country=="Drinkland"]<- NA_real_
data$satisfaction[data$method %in% c("email","web_form")]<- NA_real_


na_randomizer <- sample(c(0,1), size=sum(!is.na(data$satisfaction)), replace=TRUE, prob= c(0.1,0.9))
data$satisfaction[which(!is.na(data$satisfaction))[ na_randomizer==0]]<- NA_real_

#We see the results
summary(data$satisfaction)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   1.00    3.00    4.00    3.46    4.00    5.00   26557 

2. Exploration of Satisfaction and Probability to Recommend across Methods, Reasons, and Countries

2.1. Measurement considerations

Our main goal was to understand customer satisfaction. But, we didn’t have satisfaction data readily available for all countries and contact methods. Given this limitation, and because our key stakeholders were already familiar with it, we decided to use the Net Promoter Score (NPS) as our primary indicator.

NPS measures customer loyalty and how likely they are to recommend our services. It gave us a consistent way to evaluate customer perception across all the different areas we were interested in. For this analysis, NPS acted as a good proxy for overall customer experience and satisfaction.

To make sure NPS was a reliable stand-in for satisfaction, I took a few key steps:

  • I checked for a strong correlation between satisfaction scores and NPS scores. I looked at whether the satisfaction score was correlated with both, the raw scores (the 0-10 scale used for NPS) and the categorical scores (the percentage of Promoters minus Detractors for NPS).

  • For cases where we did have satisfaction data, I ran parallel analyses using those scores. This helped me see if the trends and insights matched what we found with NPS, or if any new patterns emerged.

2.1.1. Correlations between satisfaction scores and NPS scores

We first explored the distribution shapes for both satisfaction and nps scores, using bar charts from the ggplot2 package:

# Distribution of the NPS scores: 

ggplot(data[!is.na(data$nps.score), ], aes(x = factor(nps.score))) +
  geom_bar(fill = "#2980b9") +
  labs(
    title = "Distribution of NPS Scores",
    x = "NPS Score",
    y = "Count"
  ) +
  theme_minimal()


# Distribution of satisfaction scores: 

ggplot(data[!is.na(data$satisfaction), ], aes(x = factor(satisfaction))) +
  geom_bar(fill = "#27ae60") +
  labs(
    title = "Distribution of Satisfaction",
    x = "Satisfaction Score",
    y = "Count"
  ) +
  theme_minimal()

Neither of the distributions follows a normal distribution, with both nps.score and satisfaction showing left-skewed patterns. Notably, the distribution of nps.score also shows signs of a ceiling effect, with a considerable proportion of scores concentrated at the upper end of the scale.

Given these distributional characteristics, Pearson’s correlation is not an appropriate choice, as it assumes linearity and approximate normality, and can be highly sensitive to skewed or bounded data.

Before selecting a more robust correlation measure—such as Spearman’s rank or polychoric correlation—we will inspect the relationship between nps.score and satisfaction using a scatterplot. This will allow us to visually assess the form and strength of the association and decide whether the relationship appears monotonic or not.

# create the scatterplot (using the package ggplot 2)
ggplot(na.omit(data[, c("nps.score", "satisfaction")]), aes(x = jitter(nps.score), y = jitter(satisfaction))) +
  geom_point(alpha = 0.5, color = "#2c3e50") +
  labs(
    title = "Relation between NPS Scores and Satisfaction",
    x = "NPS Score",
    y = "Satisfaction"
  ) +
  theme_minimal()

NA
NA

The scatterplot reveals a strong monotonic and linear relationship between NPS score and Satisfaction. Considering that both variables are ordinal, we will use Spearman’s rank correlation, which assesses the strength of monotonic associations without requiring interval-level measurement or normal distribution.

# The Spearman correlation (using the psych package): 
with(data, corr.test(nps.score, satisfaction, method="spearman", use= "complete.obs"))
Call:corr.test(x = nps.score, y = satisfaction, use = "complete.obs", 
    method = "spearman")
Correlation matrix 
[1] 0.82
Sample Size 
[1] 13443
These are the unadjusted probability values.
  The probability values  adjusted for multiple tests are in the p.adj object. 
[1] 0

 To see confidence intervals of the correlations, print with the short=FALSE option

Our analysis revealed a strong positive Spearman correlation between satisfaction and NPS scores, which provides confidence that NPS effectively serves as a proxy for customer satisfaction in this dataset.

##2.2. Heatmaps of Scores Across Methods, Reasons, and Countries

Since we had many levels across contact methods, contact reasons and countries, we wanted to create a heatmap that would allow us and our stakeholders to explore the effects of these factors and their interactions.

###2.2.1. Reusable function to get heatmaps across factors

We first specified a function to obtain these heatmaps for all of our dependent variables, and for any function we wanted to apply (e.g., mean, NPS categorical…).


f.heatmap.3f <- function(my.data, my.dv, factor.columns, factor.rows, factor.blocks, my.function){


# Create the list object to keep the results, and an object to index them (starting with 1)
results <- list()
k <- 1


#I obtain the unique levels of the factors
factor.column.levels <- unique(my.data[[factor.columns]])
factor.rows.levels <- unique(my.data[[factor.rows]])
factor.blocks.levels <- unique(my.data[[factor.blocks]])


# 1. Results for a general block that does not desagregate results by the block factor
    
#1.1. First line with totals
#it will contain the value for the whole sample, and this total desagregated by the column factor

##1.1.1 Create the label for specifying we refer to results of the whole sample
row.label.t <- paste0("Total all sample")

##1.1.2. Calculate the total results for the whole sample
vector.total <- my.data[[my.dv]] # vector with all the scores
vector.total.clean <- vector.total [!is.na(vector.total)] # vector without NAs
if (length(vector.total.clean) >0){
  total <- my.function(vector.total.clean)
} else {stop("The dataset is empty")
}

##1.1.3. Calculate the total results for the columns factor
c.total <- sapply(factor.column.levels, function(c){
  vector.c.total <- my.data[ my.data[[factor.columns]] == c, my.dv ]
  vector.c.total.clean <- vector.c.total [!is.na(vector.c.total)]
  if (length(vector.c.total.clean) > 0){
    my.function(vector.c.total.clean)
  }else{
    NA_real_
  }
})

## 1.1.4. Save the results of this line in a named vector
results[[k]] <- c(group = row.label.t, overall = total, setNames(object= c.total, factor.column.levels) ) 
k <- k+1

# 1.2. Create additional lines for the general block
      #each line will shows results of the whole sample for each level of the row factor
      #and additional columns for that result desagregated by the colums factor 

##1.2.1. Create a loop for each level in the row factor
for (r in factor.rows.levels){
  
  ##1.2.2. Create a label for each line
  row.label.r <- paste0(r)
  
  ##1.2.3. Calculate the total for each level of the row factor
  r.total.vector <- my.data[my.data[[factor.rows]]==r, my.dv ]
  r.total.vector.clean <- r.total.vector [!is.na(r.total.vector)]
  if(length(r.total.vector.clean)>0){
    r.total <- my.function(r.total.vector.clean)
  } else{
    r.total <- NA_real_
  }
  
  ##1.2.4. Calculate the value for each level in the row factor
  rc.crossed <- sapply(factor.column.levels, function(c){
    rc.crossed.vector <- my.data[my.data[[factor.rows]] == r &
                                  my.data[[factor.columns]] == c,
                                  my.dv]
    rc.crossed.vector.clean <- rc.crossed.vector [!is.na(rc.crossed.vector)]
    
    if(length(rc.crossed.vector.clean)>0){
    my.function(rc.crossed.vector.clean)
    }else{
    NA_real_
    }
  })

  ##1.2.5. We save the results
  results[[k]] <- c(group = row.label.r, overall = r.total, setNames(object=rc.crossed, factor.column.levels))
  k<- k+1
  
} # Close the loop for the levels of the row factor



# 2.Results for each level of the block factor

#2.1. first line with totals
# it will contain the results for the total of the block and that result desagregated by the column factor

# 2.1.1. We create a loop for each block
for (b in factor.blocks.levels){
  
  ## 2.1.2.Label the line to put the result for the block 
  row.label.b <- paste0("Total: ", b)
  
  ## 2.1.3.Calculate the total for the block
  b.vector <- my.data[ my.data[[factor.blocks]]== b, my.dv]
  b.vector.clean <- b.vector[!is.na(b.vector)]
  if(length(b.vector.clean)>0){
    b.total <- my.function(b.vector.clean)
  }else{
    b.total <-NA_real_
  }
  

  ## 2.1.4.Calculate the results for the block desagregated by the columns factor
  bc.crossed <- sapply(factor.column.levels, function(c){
    bc.crossed.vector <- my.data [ my.data[[factor.blocks]] == b &
                                  my.data[[factor.columns]] == c, 
                                  my.dv]
    bc.crossed.vector.clean <- bc.crossed.vector[!is.na(bc.crossed.vector)]
    if(length(bc.crossed.vector.clean)>0) {my.function(bc.crossed.vector.clean)
      }else{NA_real_}
  }) 
  
  ## 2.1.5. Save the results for the block in a named vector
  results [[k]] <- c(group = row.label.b, overall= b.total, setNames(object=bc.crossed, factor.column.levels))
  k<- k+1 
 
  
      # 2.2. Add the remaining lines corresponding to each block.
      #each line will shows the total results for each level of the row factor within that block, 
      #and that result desagregated by the column factor.     
  
      ## 2.2.1. Create a sub-loop for rows, within the prior loop of blocks 
      for(r in factor.rows.levels){ 
    
        ## 2.2.Label each line specifying the block and row crossed 
        row.label.br <- paste0(b, ": ", r)  
        
        ## 2.3.Obtain the result for the block and row factors crossed 
        br.crossed.vector <- my.data[my.data[[factor.blocks]] == b &
                                      my.data[[factor.rows]] == r,
                                      my.dv]
        br.crossed.vector.clean <- br.crossed.vector[!is.na(br.crossed.vector)]
        if(length(br.crossed.vector.clean)>0){
          br.crossed <- my.function (br.crossed.vector.clean)
        }else{
          br.crossed <- NA_real_
        }
        

        ## 2.4. Obtain the result for the cross of the three factors
        all.crossed <- sapply(factor.column.levels, function(c){
        all.crossed.vector <- my.data[my.data[[factor.blocks]] == b & 
                                    my.data[[factor.rows]]==r &
                                    my.data[[factor.columns]] == c,
                                    my.dv]
        all.crossed.vector.clean <- all.crossed.vector[!is.na(all.crossed.vector)]
          if (length(all.crossed.vector.clean) > 0){my.function(all.crossed.vector.clean)}
            else {NA_real_} #
        })
    
        ## 2.5. Save the results of each line in a named vector
        results [[k]] <- c(group= row.label.br, overall = br.crossed, setNames(object=all.crossed, factor.column.levels))
        k <- k+1
        
      } ###close the loop for the rows factor
 
} ### close the loop for the blocks factor


  
# 3. We agregate the results in a dataframe and return the dataframe

results.df <- as.data.frame(do.call(rbind, results), stringsAsFactors = FALSE)

results.df[,-1] <- lapply(results.df[,-1],as.numeric)

return(results.df)
}

2.2.2. Heatmap for NPS in categorical scores

To obtain the NPS in categorical terms, we also created a simple reusable function to obtain these scores from any vector of nps raw scores.


f.nps.cat <- function (my.nps.vector, na.rm=TRUE){ # my.nps.vector is a numeric vector of raw NPS scores. It can contain NAs
  
  vector.c <- my.nps.vector[!is.na(my.nps.vector)] # vector clean of NAs
  
  # error messages if sample size = 0 or if there are scores out of the typical range of nps raw scores:
  if(length(vector.c) == 0) {stop("error: no available nps raw scores")} 
  if(length(vector.c[vector.c > 10]) > 0) {stop("error: scores higher than 10 in the nps raw scores")} 
  if(length(vector.c[vector.c < 0]) > 0) {stop("error: scores lower than 0 in the nps raw scores")} 
  
  #warning if sample size is below 70
  if(length(vector.c) < 70) {warning("few scores available for calculation (n<70)")} 
  
  n <- length(vector.c) # sample size (without missing data)
  prop.promoters <- (length(vector.c[vector.c>=9]))/n # proportion of promoters
  prop.detractors <- (length(vector.c[vector.c<=6]))/n #proportion of detractors
  nps.cat <- (prop.promoters - prop.detractors)*100 # nps score in categorical terms
  
  return (nps.cat) #returns nps in categorical terms
}

#Check if it is working as expected

f.nps.cat(data$nps.score)
[1] -14.9125
with(data, prop.table(table(nps.segment)))
nps.segment
detractor   passive  promoter 
 0.446450  0.256225  0.297325 

The function is working correctly, we can see that the score we obtain is equal to substracting the percentage of promoters and detractors that we calculated with the table function on the nps.segment variable.

Yet, before getting results, we check if our sample sizes are acceptable and not prone to sampling biases


nps.cat.heatmap.n <- f.heatmap.3f (
  my.data = data,
  my.dv = "nps.score",
  factor.columns = "method",
  factor.rows = "reason",
  factor.blocks = "country",
  my.function = length
)

All sample sizes are high across all cells (n > 150), suggesting little probability of sampling error.

Now we are ready to calculate the heatmap for the NPS categorical using the functions we have prepared.


nps.cat.heatmap <- f.heatmap.3f (
  my.data = data,
  my.dv = "nps.score",
  factor.columns = "method",
  factor.rows = "reason",
  factor.blocks = "country",
  my.function = f.nps.cat #Calculation of nps categorical values
)

# We export it to excel to add colors with conditional formatting
write.xlsx (nps.cat.heatmap, "nps.cat.heatmap.xlsx", colnames = TRUE) 

After applying conditional formatting in excel based on colors, we get our heatmap to identify areas in which probability to recommend our customer service is very low:

knitr::include_graphics("Heatmap.nps.cat.png")

The observation of the heatmap suggest us several effects: - Customers in Drinkland have lower probability to promote than Eatland. - Customers contacting for cancellations have lower probability to promote than customers calling for other reasons. - Phone is the contact method associated with highest probability to promote, while emai is associated with the lowest. - Customers contacting through chat for incorrect item deliveries feel very low probability to promote.

Yet, before we proceed to further investigate these effects, we check if we find similar patterns with other measures. The NPS categorical, although have the advantage of being familiar to our stakeholders, has several limitations, such as potentially leading to confusions because of the information lost by the % substraction (it is based on substracting the % of promoters, those who score between 9 and 10, to the % of detractors, those who score between 1 and 6). To illustrate this limitation, a categorical NPS score of 20 can come from infinite distributions of NPS raw scores, including some as different as : - distribution a) 60% of promoters and 20% of detractors - distribution b) 20% of promoters and 0% of detractors.

2.2.3. Heatmap for NPS in raw scores

To make sure the NPS categorical is not leading us to confusing patterns and ambigüities, we check if we obtain similar patterns with the NPS raw score. We apply our premaid heatmap formula to the mean of the NPS raw scores


nps.raw.heatmap <- f.heatmap.3f (
  my.data = data,
  my.dv = "nps.score",
  factor.columns = "method",
  factor.rows = "reason",
  factor.blocks = "country",
  my.function = mean # calculation of the mean of the nps raw scores
)
# Export it to excel to add colors with conditional formatting
write.xlsx (nps.raw.heatmap, "nps.raw.heatmap.xlsx", colnames = TRUE)

# We visualize both heatmaps after adding the colors in excel: 

knitr::include_graphics("Heatmap.nps2.png")

NA

We can see a similar pattern for both ways to calculate the results of the NPS, as we described in the section x.

Yet, we still have doubts about the quality of the NPS as a measure, in which customers ask for the probability to promote. To further gain confidence on it, we check its correspondence with the satisfaction scores.

2.2.2. Heatmap for satisfaction scores

For satisfaction we only had scores for one country (Eatland) and for 2 contact methods (phone and chat). However, these scores are expected to be a more valid measures of satisfaction than the NPS scores, so we will use them to see if they show a different pattern than the NPS scores.

For that, we apply the function we created in the prior section to the satisfaction scores, and we save the obtained dataframe into excel to further clean and include colors in the heatmap.


satisfaction.heatmap <- f.heatmap.3f (
  my.data = data,
  my.dv = "satisfaction", 
  factor.columns = "method",
  factor.rows = "reason",
  factor.blocks = "country",
  my.function = mean # calculation of the mean for the satisfaction scores
)

# Export it to excel to add colors with conditional formatting
write.xlsx (satisfaction.heatmap, "satisfaction.heatmap.xlsx", colnames = TRUE)

# We visualize all heatmaps after adding the colors in excel: 
knitr::include_graphics("Heatmap.png")

NA
NA

We can see a similar pattern for satisfaction and both ways to calculate the results of the NPS, suggesting that our NPS categorical measure in this context, is a good indicator of satisfaction, and that we can simplify our conversations with stakeholders by focusing on this measure.

Yet, to make sure our sight is not tricking us, we also check how the results are correlated

###2.2.4. Correlation coefficients between NPS measures and satisfaction

To see the strength of the correlations we will calculate Pearson correlations across the three measures for each cell in the heatmap. For that, we first move all the cells of the heatmaps databases in a single column, then apply the correlation.

#Move all cells in the heatmaps to a single column                                                
sat.long <- satisfaction.heatmap %>% pivot_longer(cols = c(-1),
                                 names_to = "evaluation",
                                 values_to = "satisfaction") # results of satisfaction heatmap in a single column

nps.raw.long <- nps.raw.heatmap %>% pivot_longer(cols = c(-1),
                                 names_to = "evaluation",
                                 values_to = "nps.raw")  # results of nps raw heatmap in a single column

nps.cat.long <- nps.cat.heatmap %>% pivot_longer(cols = c(-1),
                                 names_to = "evaluation",
                                 values_to = "nps.cat")  # results of nps cat heatmap in a single column

#Merge the columns for all measurs in a single dataset
merged.heatmaps <-merge(nps.cat.long, (merge(sat.long, nps.raw.long, by= c("group", "evaluation"))), by =c ("group", "evaluation"))

#Apply Pearson correlations
cor(merged.heatmaps[,3:5], use="complete.obs") #Correlation matrix across the results for the three measures
               nps.cat satisfaction   nps.raw
nps.cat      1.0000000    0.9571819 0.9854577
satisfaction 0.9571819    1.0000000 0.9850182
nps.raw      0.9854577    0.9850182 1.0000000

All measures appear to be strongly correlated (r > .90), giving us confidence on the NPS categorical being a good indicator of satisfaction and probability to promote. This is a good new because our stakeholders are more familiar with this measure, and we can use its heatmap to discuss effects with them.

LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIFNhdGlzZmFjdGlvbiBTdXJ2ZXkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCiMgMC4gU2V0dXANCg0KYGBge3J9DQpsaWJyYXJ5KHBzeWNoKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZWFkeGwpI3RvIHNlZSB0aGUgZGF0YWJhc2UNCmxpYnJhcnkob3Blbnhsc3gpI3RvIHNhdmUgdGhlIGRhdGFiYXNlIGluIGV4Y2VsDQpsaWJyYXJ5KGRwbHlyKSAjIGZvciBkYXRhIG1hbmlwdWxhdGlvbg0KbGlicmFyeSh0aWR5cikgIyBmb3IgdHJhbnNmb3JtaW5nIGRhdGFzZXRzIHRvIGxvbmcgYW5kIHdpZGUgZm9ybWF0cw0KbGlicmFyeShvcmRpbmFsKSAjIGZvciBvcmRpbmFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCmxpYnJhcnkoY2FyKSAjIGZvciBWSUYgY2FsY3VsYXRpb25zDQpsaWJyYXJ5KHZjZCkgIyBmb3IgdmlzdWFsaXppbmcgcmVsYXRpb25zIGJldHdlZW4gZmFjdG9ycw0KbGlicmFyeShWR0FNKQ0KbGlicmFyeShjb2VmcGxvdCkgIyBmb3IgcGxvdHMgYWJvdXQgY29lZmZpY2llbnRzDQpgYGANCg0KDQojIDEuIERhdGEgU2ltdWxhdGlvbg0KDQojIyAxLjEuIFNpbXVsYXRpb24gb2YgZGVtb2dyYXBoaWMgYW5kIGNvbnRleHR1YWwgdmFyaWFibGVzDQpWYXJpYWJsZXMgPC0gYygiZ2VuZGVyIiwgImFnZSIsICJtZXRob2QiLCAiY291bnRyeSIsICJyZWFzb24iLCAibnBzX3Njb3JlIiwgInNhdGlzZmFjdGlvbiIsICJjb21wbGFpbi5jb25uZWN0IiwgImNvbXBsYWluLnJlc3BvbmQudmVsIiwgImNvbXBsYWluLnNvbHZlLnZlbCIsICJjb21wbGFpbi5yZXBlYXQiICkNCg0KYGBge3J9DQoNCnNldC5zZWVkKDEwMDApDQoNCmRhdGEubiA8LSA1KjQqMioxMDAwDQoNCmRhdGEgPC0gZGF0YS5mcmFtZSAoaWQgPSBhcy5mYWN0b3IoYygxOmRhdGEubikpKQ0KDQoNCmRhdGEkYWdlIDwtIHJvdW5kKHJsbm9ybShuPWRhdGEubiwgbWVhbmxvZz0gbG9nKDQwKSwgc2Rsb2c9bG9nKDEuNCkpKQ0KZGF0YSRhZ2UgW2RhdGEkYWdlPjkwXSA8LSA5MA0KZGF0YSRhZ2UgW2RhdGEkYWdlPDE1XSA8LSAxNQ0KDQpkYXRhJGdlbmRlciA8LSBmYWN0b3Ioc2FtcGxlKGMoIm1hbiIsICJ3b21hbiIpLCBwcm9iPSBjKDAuNSwgMC41KSwgcmVwbGFjZT0gVFJVRSwgc2l6ZSA9IGRhdGEubikpDQpkYXRhJGNvdW50cnkgPC0gZmFjdG9yKHNhbXBsZShjKCJEcmlua2xhbmQiLCJFYXRsYW5kIiksIHByb2I9IGMoMC41LCAwLjUpLCByZXBsYWNlPSBUUlVFLCBzaXplID0gZGF0YS5uKSkNCmRhdGEkbWV0aG9kIDwtIGZhY3RvcihzYW1wbGUoYygiY2hhdCIsICJ3ZWJfZm9ybSIsICJlbWFpbCIsICJwaG9uZSIpLCBwcm9iPSBjKDAuNCwgMC4xMCwwLjE1LCAwLjM1KSwgcmVwbGFjZT0gVFJVRSwgc2l6ZSA9IGRhdGEubikpDQpkYXRhJHJlYXNvbiA8LSBmYWN0b3Ioc2FtcGxlKGMoImluY29ycmVjdF9pdGVtIiwgImRlbGF5IiwgInN0YXR1cyIsImNhbmNlbGxhdGlvbiIsICJvdGhlciIpLCBwcm9iPSBjKDAuMiwgMC4zLDAuMiwwLjEsIDAuMiksIHJlcGxhY2U9IFRSVUUsIHNpemUgPSBkYXRhLm4pKQ0KZGF0YSRvcGVuLmNvbW1lbnQgPC0gcmVwKCJibGEgYmxhIiwgZGF0YS5uKQ0KDQoNCmBgYA0KDQojIyBTaW11bGF0aW9uIG9mIGZyZXF1ZW5jeSBvZiBjb21wbGFpbnRzIGRlcGVuZGluZyBvZiB0aGUgZGVtb2dyYXBoaWMgYW5kIGNvbnRleHR1YWwgZmFjdG9ycw0KDQpXZSB3aWxsIHNpbXVsYXRlIHRoZSBwcmVzZW5jZSBvZiBjb21wbGFpbnRzIGFib3V0IHRoZSBjb25uZWN0aW9uIChjb21wbGFpbi5jb25uZWN0KSwgdGhlIHNwZWVkIHRvIHJlc3BvbmQgKGNvbXBsYWluLnNwLnJlc3BvbmQpLCB0aGUgc3BlZWQgdG8gc29sdmUgdGhlIHByb2JsZW0gKGNvbXBsYWluLnNwLnNvbHZlKSBhbmQgY29tcGxhaW5zIGFib3V0IHJlcGVhdGluZyBpbmZvcm1hdGlvbiAoY29tcGxhaW4ucmVwZWF0KSwgYXR0ZW5kaW5nIHRvIHNldmVyYWwgZmFjdG9yczogDQoNCi0gY29tcGxhaW50cyBhYm91dCBjb25uZWN0aW9uIHdpbGwgYmUgaGlnaGVyIGluIHRoZSBjb3VudHJ5IERyaW5rbGFuZA0KLSBjb21wbGFpbnRzIGFib3V0IHNwZWVkIHRvIHJlc3BvbnNlIHdpbGwgc3Ryb25nbHkgaGlnaGVyIGluIGVtYWlsLCBhbmQgbW9kZXJhdGVseSBoaWdoZXIgaW4gY2hhdCBhbmQgd2ViIGZvcm1zLiANCi0gY29tcGxhaW50cyBhYm91dCBzcGVlZCB0byBzb2x2ZSB3aWxsIGJlIGhpZ2hlciBpbiBpbmNvcnJlY3QgaXRlbSBkZWxpdmVyaWVzIGF0dGVuZGVkIHRocm91Z2ggY2hhdC4NCi0gY29tcGxhaW50cyBhYm91dCB0aGUgbmVlZCB0byByZXBlYXQgaW5mb3JtYXRpb24gd2lsbCBiZSBoaWdoZXIgaW4gb3JkZXIgY2FuY2VsbGF0aW9uLiANCi0gb3RoZXIgY29tcGxhaW50cyBoYXZlIHNpbWlsYXIgZGlzdHJpYnV0aW9uIGFjcm9zcyBjb3VudHJpZXMsIGNvbnRhY3QgcmVhc29ucyBhbmQgY29udGFjdCBtZXRob2RzDQoNCkZvciBlYWNoIHR5cGUgb2YgY29tcGxhaW50IHdlIHdpbGwgc2V0IGFuIGluaXRpYWwgcHJvYmFiaWxpdHksIHRoZW4gd2Ugd2lsbCBhbHRlciB0aGF0IHByb2JhYmlsaXR5IGRlcGVuZGluZyBvbiB0aGUgcHJlc2VuY2Ugb2YgdGhlIG1lbnRpb25lZCBmYWN0b3JzLiANCg0KYGBge3J9DQoNCiMjRm9yIGNvbm5lY3Rpb24gY29tcGxhaW50cw0KIyB3ZSBzZXQgYW4gaW5pdGlhbCBwcm9iYWJpbGl0eSBvZiAwLjEgb2YgdGhlIGNvbXBsYWludHMgdG8gYXBwZWFyDQpjb24ucHJvYiA8LSByZXAoMC4xLGRhdGEubikgDQojIHdlIHNldCBhIGRvdWJsZSBwcm9iYWJpbGl0eSBvZiBjb25uZWN0aW9uIGNvbXBsYWlucyBpbiBEcmlua2xhbmQNCmNvbi5wcm9iIFtkYXRhJGNvdW50cnk9PSJEcmlua2xhbmQiXSA8LSBjb24ucHJvYiBbZGF0YSRjb3VudHJ5PT0iRHJpbmtsYW5kIl0gKjIgDQojV2UgZW5zdXJlIHRoZSBwcm9iYWJpbGl0aWVzIGFyZSBjb250YWluZWQgYmV0d2VlbiAwIGFuZCAxIChmb3IgcHJldmVudGlvbiBpbiBmdXJ0aGVyIHRyYW5zZm9ybWF0aW9ucykNCmNvbi5wcm9iW2Nvbi5wcm9iPjFdIDwtIDENCmNvbi5wcm9iW2Nvbi5wcm9iPDBdIDwtIDANCiN3ZSBjcmVhdGUgdGhlIHZhcmlhYmxlIGF0dGVuZGluZyB0aGUgYWJvdmUgcHJvYmFiaWxpdGllcyBmb3IgZWFjaCBvZiBvdXIgY2FzZXMNCmRhdGEkY29tcGxhaW4uY29ubmVjdDwtIHJiaW5vbShuPWRhdGEubiwgc2l6ZSA9MSwgcHJvYj1jb24ucHJvYikNCiN0byBjaGVjayB0aGUgcmVzdWx0cyBhcmUgYXMgZXhwZWN0ZWQ6IA0Kd2l0aChkYXRhLHByb3AudGFibGUodGFibGUoY29tcGxhaW4uY29ubmVjdCxjb3VudHJ5KSwgbWFyZ2luPTIpKQ0KDQoNCiMjRm9yIGNvbXBsYWludHMgYWJvdXQgdGhlIHNwZWVkIHRvIHJlc3BvbmQNCiMgd2Ugc2V0IGFuIGluaXRpYWwgcHJvYmFiaWxpdHkgb2YgMC4wNSBvZiB0aGUgY29tcGxhaW50cyB0byBhcHBlYXINCnNwci5wcm9iIDwtIHJlcCgwLjA1LGRhdGEubikgDQojIHdlIHNldCBhIDYgdGltZXMgaGlnaGVyIHByb2JhYmlsaXR5IGZvciBjb21wbGFpbnMgdG8gYXBwZWFyIGZvciBlbWFpbA0Kc3ByLnByb2IgW2RhdGEkbWV0aG9kPT0iZW1haWwiXSA8LSBzcHIucHJvYiBbZGF0YSRtZXRob2Q9PSJlbWFpbCJdICo2IA0KI1dlIHNldCBhIDMgdGltZXMgaGlnaGVyIHByb2JhYmlsaXR5IGZvciBjb21wbGFpbnRzIHRvIGFwcGVhciBmb3IgY2hhdCBhbmQgd2ViIGZvcm1zDQpzcHIucHJvYiBbZGF0YSRtZXRob2QgJWluJSBjKCJjaGF0IiwgIndlYl9mb3JtIildIDwtIHNwci5wcm9iIFtkYXRhJG1ldGhvZCAlaW4lIGMoImNoYXQiLCAid2ViX2Zvcm0iKV0gKjMNCiNXZSBlbnN1cmUgdGhlIHByb2JhYmlsaXRpZXMgYXJlIGNvbnRhaW5lZCBiZXR3ZWVuIDAgYW5kIDEgKGZvciBwcmV2ZW50aW9uIGluIGZ1cnRoZXIgdHJhbnNmb3JtYXRpb25zKQ0Kc3ByLnByb2Jbc3ByLnByb2I+MV0gPC0gMQ0Kc3ByLnByb2Jbc3ByLnByb2I8MF0gPC0gMA0KI3dlIGNyZWF0ZSB0aGUgdmFyaWFibGUgYXR0ZW5kaW5nIHRoZSBhYm92ZSBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG9mIG91ciBjYXNlcw0KZGF0YSRjb21wbGFpbi5zcC5yZXNwb25kPC0gcmJpbm9tKG49ZGF0YS5uLCBzaXplID0xLCBwcm9iPXNwci5wcm9iKQ0KI3RvIGNoZWNrIHRoZSByZXN1bHRzIGFyZSBhcyBleHBlY3RlZDogDQp3aXRoKGRhdGEscHJvcC50YWJsZSh0YWJsZShjb21wbGFpbi5zcC5yZXNwb25kLG1ldGhvZCksbWFyZ2luPTIpKQ0KDQoNCiMjRm9yIGNvbXBsYWludHMgYWJvdXQgdGhlIHNwZWVkIHRvIHNvbHZlDQojIHdlIHNldCBhbiBpbml0aWFsIHByb2JhYmlsaXR5IG9mIDAuMSBvZiB0aGUgY29tcGxhaW50cyB0byBhcHBlYXINCnNwcy5wcm9iIDwtIHJlcCgwLjEsZGF0YS5uKSANCiMgd2Ugc2V0IGEgNSB0aW1lcyBoaWdoZXIgcHJvYmFiaWxpdHkgZm9yIGNvbnRhY3QgcmVhc29ucyBvZiBpbmNvcnJlY3QgaXRlbXMgdGhhdCBhcmUgYXR0ZW5kZWQgYnkgY2hhdA0Kc3BzLnByb2IgW2RhdGEkbWV0aG9kPT0iY2hhdCIgJiBkYXRhJHJlYXNvbj09ImluY29ycmVjdF9pdGVtIl0gPC0gc3BzLnByb2IgW2RhdGEkbWV0aG9kPT0iY2hhdCIgJiBkYXRhJHJlYXNvbj09ImluY29ycmVjdF9pdGVtIl0gKjUNCiNXZSBlbnN1cmUgdGhlIHByb2JhYmlsaXRpZXMgYXJlIGNvbnRhaW5lZCBiZXR3ZWVuIDAgYW5kIDEgKGZvciBwcmV2ZW50aW9uIGluIGZ1cnRoZXIgdHJhbnNmb3JtYXRpb25zKQ0Kc3BzLnByb2Jbc3BzLnByb2I+MV0gPC0gMQ0Kc3BzLnByb2Jbc3BzLnByb2I8MF0gPC0gMA0KI3dlIGNyZWF0ZSB0aGUgdmFyaWFibGUgYXR0ZW5kaW5nIHRoZSBhYm92ZSBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG9mIG91ciBjYXNlcw0KZGF0YSRjb21wbGFpbi5zcC5zb2x2ZTwtIHJiaW5vbShuPWRhdGEubiwgc2l6ZSA9MSwgcHJvYj1zcHMucHJvYikNCiN0byBjaGVjayB0aGUgcmVzdWx0cyBhcmUgYXMgZXhwZWN0ZWQ6IA0Kd2l0aChkYXRhLHByb3AudGFibGUodGFibGUoY29tcGxhaW4uc3Auc29sdmUscmVhc29uLG1ldGhvZCksbWFyZ2luPWMoMjozKSkpDQoNCiMjRm9yIGNvbXBsYWludHMgYWJvdXQgdGhlIG5lZWQgdG8gcmVwZWF0IGluZm9ybWF0aW9uDQojIHdlIHNldCBhbiBpbml0aWFsIHByb2JhYmlsaXR5IG9mIDAuMDIgb2YgdGhlIGNvbXBsYWludHMgdG8gYXBwZWFyDQpycC5wcm9iIDwtIHJlcCgwLjAyLGRhdGEubikgDQojIHdlIHNldCBhIDEwIHRpbWVzIGhpZ2hlciBwcm9iYWJpbGl0eSBmb3IgY29udGFjdCByZWFzb25zIGFib3V0IGNhbmNlbGxhdGlvbnMNCnJwLnByb2IgW2RhdGEkcmVhc29uPT0iY2FuY2VsbGF0aW9uIl0gPC0gcnAucHJvYiBbZGF0YSRyZWFzb249PSJjYW5jZWxsYXRpb24iXSAqMTANCiNXZSBlbnN1cmUgdGhlIHByb2JhYmlsaXRpZXMgYXJlIGNvbnRhaW5lZCBiZXR3ZWVuIDAgYW5kIDEgKGZvciBwcmV2ZW50aW9uIGluIGZ1cnRoZXIgdHJhbnNmb3JtYXRpb25zKQ0KcnAucHJvYltycC5wcm9iPjFdIDwtIDENCnJwLnByb2JbcnAucHJvYjwwXSA8LSAwDQojd2UgY3JlYXRlIHRoZSB2YXJpYWJsZSBhdHRlbmRpbmcgdGhlIGFib3ZlIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggb2Ygb3VyIGNhc2VzDQpkYXRhJGNvbXBsYWluLnJlcGVhdDwtIHJiaW5vbShuPWRhdGEubiwgc2l6ZSA9MSwgcHJvYj1ycC5wcm9iKQ0KI3RvIGNoZWNrIHRoZSByZXN1bHRzIGFyZSBhcyBleHBlY3RlZDogDQp3aXRoKGRhdGEscHJvcC50YWJsZSh0YWJsZShjb21wbGFpbi5yZXBlYXQscmVhc29uKSxtYXJnaW49MikpDQoNCiMjRm9yIG90aGVyIGNvbXBsYWludHMNCg0KZGF0YSRjb21wbGFpbi5vdGhlcjwtIHJiaW5vbShuPWRhdGEubiwgc2l6ZSA9MSwgcHJvYj0uMikNCiN0byBjaGVjayB0aGUgcmVzdWx0cyBhcmUgYXMgZXhwZWN0ZWQ6IA0Kd2l0aChkYXRhLHByb3AudGFibGUodGFibGUoY29tcGxhaW4ub3RoZXIpKSkNCg0KYGBgDQoNCg0KDQoNCg0KIyNTaW11bGF0aW9uIG9mIE5QUyBzY29yZXMgDQoNCldlIGZpcnN0IGFzc3VtZSBhIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgcmVzcG9uc2VzIGluIHRoZSBpZGVhbCBjYXNlIGluIHdoaWNoIGN1c3RvbWVycyBoYXZlIG5vIGNvbXBsYWludHMuICANClRoZW4gd2UgYXBwbHkgdGhlIGNvcnJlY3Rpb25zIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvbiB3ZSBoYXZlIHNpbXVsYXRlZCBhYm91dCB0aGUgcHJlc2VuY2Ugb2YgZGlmZmVyZW50IGNvbXBsYWludHMuIA0KDQpBdCB0aGUgZW5kLCB3ZSBsaW1pdCB0aGUgc2NvcmVzIHRvIDAgYW5kIDEwIGFuZCB0cmFuc2Zvcm0gdGhlbSB0byBpbnRlZ2VycywgaW4gbGluZSB3aXRoIHJlc3RyaWN0aW9ucyBvZiBOUFMgc2NvcmVzLiANCg0KYGBge3J9DQoNCiNQcmVsaW1pbmFyeSBkaXN0cmlidXRpb24gaW4gY2FzZSBvZiBubyBjb21wbGFpbnRzLiBXZSB1c2UgYSBoaWdoIG1lYW4gdmFsdWUNCmRhdGEkbnBzLnNjb3JlIDwtIHJub3JtKGRhdGEubiwgbWVhbj05LCBzZD0gMikNCg0KDQojV2UgYWRqdXN0IHRoZSBzY29yZXMgZm9yIHRoZSBwcmVzZW5jZSBvZiBjb21wbGFpbnRzLiBXZSBhcmUgYXBwbHlpbmcgZml4ZWQgcGVuYWx0aWVzIHRvIE5QUyBzY29yZXMgYmFzZWQgb24gdGhlIHByZXNlbmNlIG9mIGNvbXBsYWludHMgKGUuZy4sIDMgcG9pbnRzIGlmIHRoZXJlIGFyZSBjb25uZWN0aW9uIGNvbXBsYWludHMpLldoaWxlIHVzaW5nIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uIGNvdWxkIGFkZCByYW5kb21uZXNzIChhbmQgcmVhbGlzbSksIHdlIG9wdCBmb3IgZml4ZWQgdmFsdWVzIHRvIG1ha2UgdGhlIHJlbGF0aW9uc2hpcHMgZWFzaWVyIHRvIGludGVycHJldC4gDQpkYXRhJG5wcy5zY29yZSA8LSBkYXRhJG5wcy5zY29yZSAtDQozICogZGF0YSRjb21wbGFpbi5jb25uZWN0IC0NCjQgKiBkYXRhJGNvbXBsYWluLnNwLnJlc3BvbmQgLQ0KNiAqIGRhdGEkY29tcGxhaW4uc3Auc29sdmUgLQ0KMiAqIGRhdGEkY29tcGxhaW4ucmVwZWF0IC0NCjEgKiBkYXRhJGNvbXBsYWluLm90aGVyIA0KDQojIHRvIGxpbWl0IHRoZSB2YWx1ZXMgYmV0d2VlbiAwIGFuZCAxMDoNCmRhdGEkbnBzLnNjb3JlW2RhdGEkbnBzLnNjb3JlPDBdIDwtIDANCmRhdGEkbnBzLnNjb3JlW2RhdGEkbnBzLnNjb3JlPjEwXSA8LSAxMCANCg0KIyB0byBjb252ZXJ0IHRvIGludGVnZXIgdmFsdWVzDQpkYXRhJG5wcy5zY29yZSA8LSBmbG9vciAoZGF0YSRucHMuc2NvcmUpDQoNCg0KI3RvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNjb3Jlcw0KDQpiYXJwbG90KHRhYmxlKGRhdGEkbnBzLnNjb3JlKSkgDQoNCndpdGgoZGF0YSwocHJvcC50YWJsZSh0YWJsZShkYXRhJG5wcy5zY29yZSkpKSkNCg0KDQpgYGANCg0KDQojIyMgU3BlY2lmeWluZyB0aGUgTlBTIHNlZ21lbnRzIGJhc2VkIG9uIE5QUyBzY29yZXMNCg0KYGBge3J9DQpkYXRhJG5wcy5zZWdtZW50IDwtIHJlcChOQV9jaGFyYWN0ZXJfLCBkYXRhLm4pDQoNCmRhdGEkbnBzLnNlZ21lbnQgW2RhdGEkbnBzLnNjb3JlPD02XSA8LSAiZGV0cmFjdG9yIg0KZGF0YSRucHMuc2VnbWVudCBbZGF0YSRucHMuc2NvcmUgPjYgJiBkYXRhJG5wcy5zY29yZSA8OV0gPC0gInBhc3NpdmUiDQpkYXRhJG5wcy5zZWdtZW50IFtkYXRhJG5wcy5zY29yZT49OV0gPC0gInByb21vdGVyIg0KDQpkYXRhJG5wcy5zZWdtZW50IDwtIGZhY3RvciAoZGF0YSRucHMuc2VnbWVudCkNCg0KI3RvIGNoZWNrIHRoZSByZXN1bHRzDQp3aXRoKGRhdGEsIHByb3AudGFibGUodGFibGUobnBzLnNlZ21lbnQpKSkNCmBgYA0KDQojI1NpbXVsYXRpb24gb2Ygc2F0aXNmYWN0aW9uIHNjb3Jlcw0KDQoNCmBgYHtyfQ0KDQpkYXRhJHNhdGlzZmFjdGlvbiA8LSBmbG9vcihybm9ybShkYXRhLm4sIG1lYW4gPSAzLCBzZD0wLjUpICsgMC45ICogKDEgKyBhcy5udW1lcmljKHNjYWxlKGRhdGEkbnBzLnNjb3JlKSkpKQ0KDQpkYXRhJHNhdGlzZmFjdGlvbltkYXRhJHNhdGlzZmFjdGlvbjwxXSA8LSAxDQpkYXRhJHNhdGlzZmFjdGlvbltkYXRhJHNhdGlzZmFjdGlvbj41XSA8LSA1DQoNCiMgVG8gc2VlIHRoZSBkaXN0cmlidXRpb24NCmJhcnBsb3QodGFibGUoZGF0YSRzYXRpc2ZhY3Rpb24pKQ0Kd2l0aChkYXRhLHByb3AudGFibGUodGFibGUoc2F0aXNmYWN0aW9uKSkpDQoNCg0KIyBUbyBjaGVjayB0aGUgY29ycmVsYXRpb24gd2l0aCBOUFMNCndpdGgoZGF0YSwgY29yKG5wcy5zY29yZSxzYXRpc2ZhY3Rpb24sIG1ldGhvZD0ic3BlYXJtYW4iKSkNCg0KDQpgYGANCg0KIyNTaW11bGF0aW9uIG9mIG1pc3NpbmcgZGF0YQ0KDQojIyNNaXNzaW5nIGRhdGEgZm9yIHNhdGlzZmFjdGlvbg0KDQpXZSBzaW11bGF0ZSB0aGF0IHNhdGlzZmFjdGlvbiB3YXMgbm90IGF2YWlsYWJsZSBmb3IgdGhlIGNvdW50cnkgRHJpbmtsYW5kLCBhbmQgZm9yIHRoZSBjb250YWN0IG1ldGhvZHMgb2YgZW1haWwgYW5kIHdlYmZvcm0uIA0KQWRkaXRpb25hbGx5LCB3aXRoaW4gdGhvc2Ugc2l0dWF0aW9ucyBpbiB3aGljaCB0aGUgc2F0aXNmYWN0aW9uIGl0ZW0gd2FzIGF2YWlsYWJsZSwgdGhlcmUgaXMgMTAlIHByb2JhYmlsaXR5IHRoYXQgY3VzdG9tZXJzIGRvIG5vdCBhbnN3ZXIgdG8gaXQuDQoNCmBgYHtyfQ0KDQpkYXRhJHNhdGlzZmFjdGlvbltkYXRhJGNvdW50cnk9PSJEcmlua2xhbmQiXTwtIE5BX3JlYWxfDQpkYXRhJHNhdGlzZmFjdGlvbltkYXRhJG1ldGhvZCAlaW4lIGMoImVtYWlsIiwid2ViX2Zvcm0iKV08LSBOQV9yZWFsXw0KDQoNCm5hX3JhbmRvbWl6ZXIgPC0gc2FtcGxlKGMoMCwxKSwgc2l6ZT1zdW0oIWlzLm5hKGRhdGEkc2F0aXNmYWN0aW9uKSksIHJlcGxhY2U9VFJVRSwgcHJvYj0gYygwLjEsMC45KSkNCmRhdGEkc2F0aXNmYWN0aW9uW3doaWNoKCFpcy5uYShkYXRhJHNhdGlzZmFjdGlvbikpWyBuYV9yYW5kb21pemVyPT0wXV08LSBOQV9yZWFsXw0KDQojV2Ugc2VlIHRoZSByZXN1bHRzDQpzdW1tYXJ5KGRhdGEkc2F0aXNmYWN0aW9uKQ0KDQpgYGANCg0KDQoNCg0KDQojIDIuIEV4cGxvcmF0aW9uIG9mIFNhdGlzZmFjdGlvbiBhbmQgUHJvYmFiaWxpdHkgdG8gUmVjb21tZW5kICBhY3Jvc3MgTWV0aG9kcywgUmVhc29ucywgYW5kIENvdW50cmllcw0KDQojIyAyLjEuIE1lYXN1cmVtZW50IGNvbnNpZGVyYXRpb25zDQpPdXIgbWFpbiBnb2FsIHdhcyB0byB1bmRlcnN0YW5kIGN1c3RvbWVyIHNhdGlzZmFjdGlvbi4gQnV0LCB3ZSBkaWRuJ3QgaGF2ZSBzYXRpc2ZhY3Rpb24gZGF0YSByZWFkaWx5IGF2YWlsYWJsZSBmb3IgYWxsIGNvdW50cmllcyBhbmQgY29udGFjdCBtZXRob2RzLiBHaXZlbiB0aGlzIGxpbWl0YXRpb24sIGFuZCBiZWNhdXNlIG91ciBrZXkgc3Rha2Vob2xkZXJzIHdlcmUgYWxyZWFkeSBmYW1pbGlhciB3aXRoIGl0LCB3ZSBkZWNpZGVkIHRvIHVzZSB0aGUgTmV0IFByb21vdGVyIFNjb3JlIChOUFMpIGFzIG91ciBwcmltYXJ5IGluZGljYXRvci4NCg0KTlBTIG1lYXN1cmVzIGN1c3RvbWVyIGxveWFsdHkgYW5kIGhvdyBsaWtlbHkgdGhleSBhcmUgdG8gcmVjb21tZW5kIG91ciBzZXJ2aWNlcy4gSXQgZ2F2ZSB1cyBhIGNvbnNpc3RlbnQgd2F5IHRvIGV2YWx1YXRlIGN1c3RvbWVyIHBlcmNlcHRpb24gYWNyb3NzIGFsbCB0aGUgZGlmZmVyZW50IGFyZWFzIHdlIHdlcmUgaW50ZXJlc3RlZCBpbi4gRm9yIHRoaXMgYW5hbHlzaXMsIE5QUyBhY3RlZCBhcyBhIGdvb2QgcHJveHkgZm9yIG92ZXJhbGwgY3VzdG9tZXIgZXhwZXJpZW5jZSBhbmQgc2F0aXNmYWN0aW9uLg0KDQpUbyBtYWtlIHN1cmUgTlBTIHdhcyBhIHJlbGlhYmxlIHN0YW5kLWluIGZvciBzYXRpc2ZhY3Rpb24sIEkgdG9vayBhIGZldyBrZXkgc3RlcHM6DQoNCi0gSSBjaGVja2VkIGZvciBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHNhdGlzZmFjdGlvbiBzY29yZXMgYW5kIE5QUyBzY29yZXMuIEkgbG9va2VkIGF0IHdoZXRoZXIgdGhlIHNhdGlzZmFjdGlvbiBzY29yZSB3YXMgY29ycmVsYXRlZCB3aXRoIGJvdGgsIHRoZSByYXcgc2NvcmVzICh0aGUgMC0xMCBzY2FsZSB1c2VkIGZvciBOUFMpIGFuZCB0aGUgY2F0ZWdvcmljYWwgc2NvcmVzICh0aGUgcGVyY2VudGFnZSBvZiBQcm9tb3RlcnMgbWludXMgRGV0cmFjdG9ycyBmb3IgTlBTKS4NCg0KLSBGb3IgY2FzZXMgd2hlcmUgd2UgZGlkIGhhdmUgc2F0aXNmYWN0aW9uIGRhdGEsIEkgcmFuIHBhcmFsbGVsIGFuYWx5c2VzIHVzaW5nIHRob3NlIHNjb3Jlcy4gVGhpcyBoZWxwZWQgbWUgc2VlIGlmIHRoZSB0cmVuZHMgYW5kIGluc2lnaHRzIG1hdGNoZWQgd2hhdCB3ZSBmb3VuZCB3aXRoIE5QUywgb3IgaWYgYW55IG5ldyBwYXR0ZXJucyBlbWVyZ2VkLg0KDQojIyAyLjEuMS4gQ29ycmVsYXRpb25zIGJldHdlZW4gc2F0aXNmYWN0aW9uIHNjb3JlcyBhbmQgTlBTIHNjb3Jlcw0KDQpXZSBmaXJzdCBleHBsb3JlZCB0aGUgZGlzdHJpYnV0aW9uIHNoYXBlcyBmb3IgYm90aCBzYXRpc2ZhY3Rpb24gYW5kIG5wcyBzY29yZXMsIHVzaW5nIGJhciBjaGFydHMgZnJvbSB0aGUgZ2dwbG90MiBwYWNrYWdlOiANCg0KYGBge3J9DQojIERpc3RyaWJ1dGlvbiBvZiB0aGUgTlBTIHNjb3JlczogDQoNCmdncGxvdChkYXRhWyFpcy5uYShkYXRhJG5wcy5zY29yZSksIF0sIGFlcyh4ID0gZmFjdG9yKG5wcy5zY29yZSkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzI5ODBiOSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTlBTIFNjb3JlcyIsDQogICAgeCA9ICJOUFMgU2NvcmUiLA0KICAgIHkgPSAiQ291bnQiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIyBEaXN0cmlidXRpb24gb2Ygc2F0aXNmYWN0aW9uIHNjb3JlczogDQoNCmdncGxvdChkYXRhWyFpcy5uYShkYXRhJHNhdGlzZmFjdGlvbiksIF0sIGFlcyh4ID0gZmFjdG9yKHNhdGlzZmFjdGlvbikpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzI3YWU2MCIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU2F0aXNmYWN0aW9uIiwNCiAgICB4ID0gIlNhdGlzZmFjdGlvbiBTY29yZSIsDQogICAgeSA9ICJDb3VudCINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpOZWl0aGVyIG9mIHRoZSBkaXN0cmlidXRpb25zIGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCB3aXRoIGJvdGggbnBzLnNjb3JlIGFuZCBzYXRpc2ZhY3Rpb24gc2hvd2luZyBsZWZ0LXNrZXdlZCBwYXR0ZXJucy4gTm90YWJseSwgdGhlIGRpc3RyaWJ1dGlvbiBvZiBucHMuc2NvcmUgYWxzbyBzaG93cyBzaWducyBvZiBhIGNlaWxpbmcgZWZmZWN0LCB3aXRoIGEgY29uc2lkZXJhYmxlIHByb3BvcnRpb24gb2Ygc2NvcmVzIGNvbmNlbnRyYXRlZCBhdCB0aGUgdXBwZXIgZW5kIG9mIHRoZSBzY2FsZS4NCg0KR2l2ZW4gdGhlc2UgZGlzdHJpYnV0aW9uYWwgY2hhcmFjdGVyaXN0aWNzLCBQZWFyc29uJ3MgY29ycmVsYXRpb24gaXMgbm90IGFuIGFwcHJvcHJpYXRlIGNob2ljZSwgYXMgaXQgYXNzdW1lcyBsaW5lYXJpdHkgYW5kIGFwcHJveGltYXRlIG5vcm1hbGl0eSwgYW5kIGNhbiBiZSBoaWdobHkgc2Vuc2l0aXZlIHRvIHNrZXdlZCBvciBib3VuZGVkIGRhdGEuDQoNCkJlZm9yZSBzZWxlY3RpbmcgYSBtb3JlIHJvYnVzdCBjb3JyZWxhdGlvbiBtZWFzdXJl4oCUc3VjaCBhcyBTcGVhcm1hbidzIHJhbmsgb3IgcG9seWNob3JpYyBjb3JyZWxhdGlvbuKAlHdlIHdpbGwgaW5zcGVjdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbnBzLnNjb3JlIGFuZCBzYXRpc2ZhY3Rpb24gdXNpbmcgYSBzY2F0dGVycGxvdC4gVGhpcyB3aWxsIGFsbG93IHVzIHRvIHZpc3VhbGx5IGFzc2VzcyB0aGUgZm9ybSBhbmQgc3RyZW5ndGggb2YgdGhlIGFzc29jaWF0aW9uIGFuZCBkZWNpZGUgd2hldGhlciB0aGUgcmVsYXRpb25zaGlwIGFwcGVhcnMgbW9ub3RvbmljIG9yIG5vdC4NCg0KYGBge3J9DQojIGNyZWF0ZSB0aGUgc2NhdHRlcnBsb3QgKHVzaW5nIHRoZSBwYWNrYWdlIGdncGxvdCAyKQ0KZ2dwbG90KG5hLm9taXQoZGF0YVssIGMoIm5wcy5zY29yZSIsICJzYXRpc2ZhY3Rpb24iKV0pLCBhZXMoeCA9IGppdHRlcihucHMuc2NvcmUpLCB5ID0gaml0dGVyKHNhdGlzZmFjdGlvbikpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIGNvbG9yID0gIiMyYzNlNTAiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUmVsYXRpb24gYmV0d2VlbiBOUFMgU2NvcmVzIGFuZCBTYXRpc2ZhY3Rpb24iLA0KICAgIHggPSAiTlBTIFNjb3JlIiwNCiAgICB5ID0gIlNhdGlzZmFjdGlvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQoNCmBgYA0KDQpUaGUgc2NhdHRlcnBsb3QgcmV2ZWFscyBhIHN0cm9uZyBtb25vdG9uaWMgYW5kIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBOUFMgc2NvcmUgYW5kIFNhdGlzZmFjdGlvbi4gQ29uc2lkZXJpbmcgdGhhdCBib3RoIHZhcmlhYmxlcyBhcmUgb3JkaW5hbCwgd2Ugd2lsbCB1c2UgU3BlYXJtYW7igJlzIHJhbmsgY29ycmVsYXRpb24sIHdoaWNoIGFzc2Vzc2VzIHRoZSBzdHJlbmd0aCBvZiBtb25vdG9uaWMgYXNzb2NpYXRpb25zIHdpdGhvdXQgcmVxdWlyaW5nIGludGVydmFsLWxldmVsIG1lYXN1cmVtZW50IG9yIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCg0KDQoNCmBgYHtyfQ0KIyBUaGUgU3BlYXJtYW4gY29ycmVsYXRpb24gKHVzaW5nIHRoZSBwc3ljaCBwYWNrYWdlKTogDQp3aXRoKGRhdGEsIGNvcnIudGVzdChucHMuc2NvcmUsIHNhdGlzZmFjdGlvbiwgbWV0aG9kPSJzcGVhcm1hbiIsIHVzZT0gImNvbXBsZXRlLm9icyIpKQ0KDQoNCmBgYA0KDQpPdXIgYW5hbHlzaXMgcmV2ZWFsZWQgYSBzdHJvbmcgcG9zaXRpdmUgU3BlYXJtYW4gY29ycmVsYXRpb24gYmV0d2VlbiBzYXRpc2ZhY3Rpb24gYW5kIE5QUyBzY29yZXMsIHdoaWNoIHByb3ZpZGVzIGNvbmZpZGVuY2UgdGhhdCBOUFMgZWZmZWN0aXZlbHkgc2VydmVzIGFzIGEgcHJveHkgZm9yIGN1c3RvbWVyIHNhdGlzZmFjdGlvbiBpbiB0aGlzIGRhdGFzZXQuIA0KDQoNCg0KIyMyLjIuIEhlYXRtYXBzIG9mIFNjb3JlcyBBY3Jvc3MgTWV0aG9kcywgUmVhc29ucywgYW5kIENvdW50cmllcw0KDQpTaW5jZSB3ZSBoYWQgbWFueSBsZXZlbHMgYWNyb3NzIGNvbnRhY3QgbWV0aG9kcywgY29udGFjdCByZWFzb25zIGFuZCBjb3VudHJpZXMsIHdlIHdhbnRlZCB0byBjcmVhdGUgYSBoZWF0bWFwIHRoYXQgd291bGQgYWxsb3cgdXMgYW5kIG91ciBzdGFrZWhvbGRlcnMgdG8gZXhwbG9yZSB0aGUgZWZmZWN0cyBvZiB0aGVzZSBmYWN0b3JzIGFuZCB0aGVpciBpbnRlcmFjdGlvbnMuIA0KDQojIyMyLjIuMS4gUmV1c2FibGUgZnVuY3Rpb24gdG8gZ2V0IGhlYXRtYXBzIGFjcm9zcyBmYWN0b3JzDQoNCldlIGZpcnN0IHNwZWNpZmllZCBhIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGVzZSBoZWF0bWFwcyBmb3IgYWxsIG9mIG91ciBkZXBlbmRlbnQgdmFyaWFibGVzLCBhbmQgZm9yIGFueSBmdW5jdGlvbiB3ZSB3YW50ZWQgdG8gYXBwbHkgKGUuZy4sIG1lYW4sIE5QUyBjYXRlZ29yaWNhbC4uLikuIA0KDQoNCg0KYGBge3J9DQoNCmYuaGVhdG1hcC4zZiA8LSBmdW5jdGlvbihteS5kYXRhLCBteS5kdiwgZmFjdG9yLmNvbHVtbnMsIGZhY3Rvci5yb3dzLCBmYWN0b3IuYmxvY2tzLCBteS5mdW5jdGlvbil7DQoNCg0KIyBDcmVhdGUgdGhlIGxpc3Qgb2JqZWN0IHRvIGtlZXAgdGhlIHJlc3VsdHMsIGFuZCBhbiBvYmplY3QgdG8gaW5kZXggdGhlbSAoc3RhcnRpbmcgd2l0aCAxKQ0KcmVzdWx0cyA8LSBsaXN0KCkNCmsgPC0gMQ0KDQoNCiNJIG9idGFpbiB0aGUgdW5pcXVlIGxldmVscyBvZiB0aGUgZmFjdG9ycw0KZmFjdG9yLmNvbHVtbi5sZXZlbHMgPC0gdW5pcXVlKG15LmRhdGFbW2ZhY3Rvci5jb2x1bW5zXV0pDQpmYWN0b3Iucm93cy5sZXZlbHMgPC0gdW5pcXVlKG15LmRhdGFbW2ZhY3Rvci5yb3dzXV0pDQpmYWN0b3IuYmxvY2tzLmxldmVscyA8LSB1bmlxdWUobXkuZGF0YVtbZmFjdG9yLmJsb2Nrc11dKQ0KDQoNCiMgMS4gUmVzdWx0cyBmb3IgYSBnZW5lcmFsIGJsb2NrIHRoYXQgZG9lcyBub3QgZGVzYWdyZWdhdGUgcmVzdWx0cyBieSB0aGUgYmxvY2sgZmFjdG9yDQogICAgDQojMS4xLiBGaXJzdCBsaW5lIHdpdGggdG90YWxzDQojaXQgd2lsbCBjb250YWluIHRoZSB2YWx1ZSBmb3IgdGhlIHdob2xlIHNhbXBsZSwgYW5kIHRoaXMgdG90YWwgZGVzYWdyZWdhdGVkIGJ5IHRoZSBjb2x1bW4gZmFjdG9yDQoNCiMjMS4xLjEgQ3JlYXRlIHRoZSBsYWJlbCBmb3Igc3BlY2lmeWluZyB3ZSByZWZlciB0byByZXN1bHRzIG9mIHRoZSB3aG9sZSBzYW1wbGUNCnJvdy5sYWJlbC50IDwtIHBhc3RlMCgiVG90YWwgYWxsIHNhbXBsZSIpDQoNCiMjMS4xLjIuIENhbGN1bGF0ZSB0aGUgdG90YWwgcmVzdWx0cyBmb3IgdGhlIHdob2xlIHNhbXBsZQ0KdmVjdG9yLnRvdGFsIDwtIG15LmRhdGFbW215LmR2XV0gIyB2ZWN0b3Igd2l0aCBhbGwgdGhlIHNjb3Jlcw0KdmVjdG9yLnRvdGFsLmNsZWFuIDwtIHZlY3Rvci50b3RhbCBbIWlzLm5hKHZlY3Rvci50b3RhbCldICMgdmVjdG9yIHdpdGhvdXQgTkFzDQppZiAobGVuZ3RoKHZlY3Rvci50b3RhbC5jbGVhbikgPjApew0KICB0b3RhbCA8LSBteS5mdW5jdGlvbih2ZWN0b3IudG90YWwuY2xlYW4pDQp9IGVsc2Uge3N0b3AoIlRoZSBkYXRhc2V0IGlzIGVtcHR5IikNCn0NCg0KIyMxLjEuMy4gQ2FsY3VsYXRlIHRoZSB0b3RhbCByZXN1bHRzIGZvciB0aGUgY29sdW1ucyBmYWN0b3INCmMudG90YWwgPC0gc2FwcGx5KGZhY3Rvci5jb2x1bW4ubGV2ZWxzLCBmdW5jdGlvbihjKXsNCiAgdmVjdG9yLmMudG90YWwgPC0gbXkuZGF0YVsgbXkuZGF0YVtbZmFjdG9yLmNvbHVtbnNdXSA9PSBjLCBteS5kdiBdDQogIHZlY3Rvci5jLnRvdGFsLmNsZWFuIDwtIHZlY3Rvci5jLnRvdGFsIFshaXMubmEodmVjdG9yLmMudG90YWwpXQ0KICBpZiAobGVuZ3RoKHZlY3Rvci5jLnRvdGFsLmNsZWFuKSA+IDApew0KICAgIG15LmZ1bmN0aW9uKHZlY3Rvci5jLnRvdGFsLmNsZWFuKQ0KICB9ZWxzZXsNCiAgICBOQV9yZWFsXw0KICB9DQp9KQ0KDQojIyAxLjEuNC4gU2F2ZSB0aGUgcmVzdWx0cyBvZiB0aGlzIGxpbmUgaW4gYSBuYW1lZCB2ZWN0b3INCnJlc3VsdHNbW2tdXSA8LSBjKGdyb3VwID0gcm93LmxhYmVsLnQsIG92ZXJhbGwgPSB0b3RhbCwgc2V0TmFtZXMob2JqZWN0PSBjLnRvdGFsLCBmYWN0b3IuY29sdW1uLmxldmVscykgKSANCmsgPC0gaysxDQoNCiMgMS4yLiBDcmVhdGUgYWRkaXRpb25hbCBsaW5lcyBmb3IgdGhlIGdlbmVyYWwgYmxvY2sNCiAgICAgICNlYWNoIGxpbmUgd2lsbCBzaG93cyByZXN1bHRzIG9mIHRoZSB3aG9sZSBzYW1wbGUgZm9yIGVhY2ggbGV2ZWwgb2YgdGhlIHJvdyBmYWN0b3INCiAgICAgICNhbmQgYWRkaXRpb25hbCBjb2x1bW5zIGZvciB0aGF0IHJlc3VsdCBkZXNhZ3JlZ2F0ZWQgYnkgdGhlIGNvbHVtcyBmYWN0b3IgDQoNCiMjMS4yLjEuIENyZWF0ZSBhIGxvb3AgZm9yIGVhY2ggbGV2ZWwgaW4gdGhlIHJvdyBmYWN0b3INCmZvciAociBpbiBmYWN0b3Iucm93cy5sZXZlbHMpew0KICANCiAgIyMxLjIuMi4gQ3JlYXRlIGEgbGFiZWwgZm9yIGVhY2ggbGluZQ0KICByb3cubGFiZWwuciA8LSBwYXN0ZTAocikNCiAgDQogICMjMS4yLjMuIENhbGN1bGF0ZSB0aGUgdG90YWwgZm9yIGVhY2ggbGV2ZWwgb2YgdGhlIHJvdyBmYWN0b3INCiAgci50b3RhbC52ZWN0b3IgPC0gbXkuZGF0YVtteS5kYXRhW1tmYWN0b3Iucm93c11dPT1yLCBteS5kdiBdDQogIHIudG90YWwudmVjdG9yLmNsZWFuIDwtIHIudG90YWwudmVjdG9yIFshaXMubmEoci50b3RhbC52ZWN0b3IpXQ0KICBpZihsZW5ndGgoci50b3RhbC52ZWN0b3IuY2xlYW4pPjApew0KICAgIHIudG90YWwgPC0gbXkuZnVuY3Rpb24oci50b3RhbC52ZWN0b3IuY2xlYW4pDQogIH0gZWxzZXsNCiAgICByLnRvdGFsIDwtIE5BX3JlYWxfDQogIH0NCiAgDQogICMjMS4yLjQuIENhbGN1bGF0ZSB0aGUgdmFsdWUgZm9yIGVhY2ggbGV2ZWwgaW4gdGhlIHJvdyBmYWN0b3INCiAgcmMuY3Jvc3NlZCA8LSBzYXBwbHkoZmFjdG9yLmNvbHVtbi5sZXZlbHMsIGZ1bmN0aW9uKGMpew0KICAgIHJjLmNyb3NzZWQudmVjdG9yIDwtIG15LmRhdGFbbXkuZGF0YVtbZmFjdG9yLnJvd3NdXSA9PSByICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteS5kYXRhW1tmYWN0b3IuY29sdW1uc11dID09IGMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXkuZHZdDQogICAgcmMuY3Jvc3NlZC52ZWN0b3IuY2xlYW4gPC0gcmMuY3Jvc3NlZC52ZWN0b3IgWyFpcy5uYShyYy5jcm9zc2VkLnZlY3RvcildDQogICAgDQogICAgaWYobGVuZ3RoKHJjLmNyb3NzZWQudmVjdG9yLmNsZWFuKT4wKXsNCiAgICBteS5mdW5jdGlvbihyYy5jcm9zc2VkLnZlY3Rvci5jbGVhbikNCiAgICB9ZWxzZXsNCiAgICBOQV9yZWFsXw0KICAgIH0NCiAgfSkNCg0KICAjIzEuMi41LiBXZSBzYXZlIHRoZSByZXN1bHRzDQogIHJlc3VsdHNbW2tdXSA8LSBjKGdyb3VwID0gcm93LmxhYmVsLnIsIG92ZXJhbGwgPSByLnRvdGFsLCBzZXROYW1lcyhvYmplY3Q9cmMuY3Jvc3NlZCwgZmFjdG9yLmNvbHVtbi5sZXZlbHMpKQ0KICBrPC0gaysxDQogIA0KfSAjIENsb3NlIHRoZSBsb29wIGZvciB0aGUgbGV2ZWxzIG9mIHRoZSByb3cgZmFjdG9yDQoNCg0KDQojIDIuUmVzdWx0cyBmb3IgZWFjaCBsZXZlbCBvZiB0aGUgYmxvY2sgZmFjdG9yDQoNCiMyLjEuIGZpcnN0IGxpbmUgd2l0aCB0b3RhbHMNCiMgaXQgd2lsbCBjb250YWluIHRoZSByZXN1bHRzIGZvciB0aGUgdG90YWwgb2YgdGhlIGJsb2NrIGFuZCB0aGF0IHJlc3VsdCBkZXNhZ3JlZ2F0ZWQgYnkgdGhlIGNvbHVtbiBmYWN0b3INCg0KIyAyLjEuMS4gV2UgY3JlYXRlIGEgbG9vcCBmb3IgZWFjaCBibG9jaw0KZm9yIChiIGluIGZhY3Rvci5ibG9ja3MubGV2ZWxzKXsNCiAgDQogICMjIDIuMS4yLkxhYmVsIHRoZSBsaW5lIHRvIHB1dCB0aGUgcmVzdWx0IGZvciB0aGUgYmxvY2sgDQogIHJvdy5sYWJlbC5iIDwtIHBhc3RlMCgiVG90YWw6ICIsIGIpDQogIA0KICAjIyAyLjEuMy5DYWxjdWxhdGUgdGhlIHRvdGFsIGZvciB0aGUgYmxvY2sNCiAgYi52ZWN0b3IgPC0gbXkuZGF0YVsgbXkuZGF0YVtbZmFjdG9yLmJsb2Nrc11dPT0gYiwgbXkuZHZdDQogIGIudmVjdG9yLmNsZWFuIDwtIGIudmVjdG9yWyFpcy5uYShiLnZlY3RvcildDQogIGlmKGxlbmd0aChiLnZlY3Rvci5jbGVhbik+MCl7DQogICAgYi50b3RhbCA8LSBteS5mdW5jdGlvbihiLnZlY3Rvci5jbGVhbikNCiAgfWVsc2V7DQogICAgYi50b3RhbCA8LU5BX3JlYWxfDQogIH0NCiAgDQoNCiAgIyMgMi4xLjQuQ2FsY3VsYXRlIHRoZSByZXN1bHRzIGZvciB0aGUgYmxvY2sgZGVzYWdyZWdhdGVkIGJ5IHRoZSBjb2x1bW5zIGZhY3Rvcg0KICBiYy5jcm9zc2VkIDwtIHNhcHBseShmYWN0b3IuY29sdW1uLmxldmVscywgZnVuY3Rpb24oYyl7DQogICAgYmMuY3Jvc3NlZC52ZWN0b3IgPC0gbXkuZGF0YSBbIG15LmRhdGFbW2ZhY3Rvci5ibG9ja3NdXSA9PSBiICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteS5kYXRhW1tmYWN0b3IuY29sdW1uc11dID09IGMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15LmR2XQ0KICAgIGJjLmNyb3NzZWQudmVjdG9yLmNsZWFuIDwtIGJjLmNyb3NzZWQudmVjdG9yWyFpcy5uYShiYy5jcm9zc2VkLnZlY3RvcildDQogICAgaWYobGVuZ3RoKGJjLmNyb3NzZWQudmVjdG9yLmNsZWFuKT4wKSB7bXkuZnVuY3Rpb24oYmMuY3Jvc3NlZC52ZWN0b3IuY2xlYW4pDQogICAgICB9ZWxzZXtOQV9yZWFsX30NCiAgfSkgDQogIA0KICAjIyAyLjEuNS4gU2F2ZSB0aGUgcmVzdWx0cyBmb3IgdGhlIGJsb2NrIGluIGEgbmFtZWQgdmVjdG9yDQogIHJlc3VsdHMgW1trXV0gPC0gYyhncm91cCA9IHJvdy5sYWJlbC5iLCBvdmVyYWxsPSBiLnRvdGFsLCBzZXROYW1lcyhvYmplY3Q9YmMuY3Jvc3NlZCwgZmFjdG9yLmNvbHVtbi5sZXZlbHMpKQ0KICBrPC0gaysxIA0KIA0KICANCiAgICAgICMgMi4yLiBBZGQgdGhlIHJlbWFpbmluZyBsaW5lcyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggYmxvY2suDQogICAgICAjZWFjaCBsaW5lIHdpbGwgc2hvd3MgdGhlIHRvdGFsIHJlc3VsdHMgZm9yIGVhY2ggbGV2ZWwgb2YgdGhlIHJvdyBmYWN0b3Igd2l0aGluIHRoYXQgYmxvY2ssIA0KICAgICAgI2FuZCB0aGF0IHJlc3VsdCBkZXNhZ3JlZ2F0ZWQgYnkgdGhlIGNvbHVtbiBmYWN0b3IuICAgICANCiAgDQogICAgICAjIyAyLjIuMS4gQ3JlYXRlIGEgc3ViLWxvb3AgZm9yIHJvd3MsIHdpdGhpbiB0aGUgcHJpb3IgbG9vcCBvZiBibG9ja3MgDQogICAgICBmb3IociBpbiBmYWN0b3Iucm93cy5sZXZlbHMpeyANCiAgICANCiAgICAgICAgIyMgMi4yLkxhYmVsIGVhY2ggbGluZSBzcGVjaWZ5aW5nIHRoZSBibG9jayBhbmQgcm93IGNyb3NzZWQgDQogICAgICAgIHJvdy5sYWJlbC5iciA8LSBwYXN0ZTAoYiwgIjogIiwgcikgIA0KICAgICAgICANCiAgICAgICAgIyMgMi4zLk9idGFpbiB0aGUgcmVzdWx0IGZvciB0aGUgYmxvY2sgYW5kIHJvdyBmYWN0b3JzIGNyb3NzZWQgDQogICAgICAgIGJyLmNyb3NzZWQudmVjdG9yIDwtIG15LmRhdGFbbXkuZGF0YVtbZmFjdG9yLmJsb2Nrc11dID09IGIgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteS5kYXRhW1tmYWN0b3Iucm93c11dID09IHIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15LmR2XQ0KICAgICAgICBici5jcm9zc2VkLnZlY3Rvci5jbGVhbiA8LSBici5jcm9zc2VkLnZlY3RvclshaXMubmEoYnIuY3Jvc3NlZC52ZWN0b3IpXQ0KICAgICAgICBpZihsZW5ndGgoYnIuY3Jvc3NlZC52ZWN0b3IuY2xlYW4pPjApew0KICAgICAgICAgIGJyLmNyb3NzZWQgPC0gbXkuZnVuY3Rpb24gKGJyLmNyb3NzZWQudmVjdG9yLmNsZWFuKQ0KICAgICAgICB9ZWxzZXsNCiAgICAgICAgICBici5jcm9zc2VkIDwtIE5BX3JlYWxfDQogICAgICAgIH0NCiAgICAgICAgDQoNCiAgICAgICAgIyMgMi40LiBPYnRhaW4gdGhlIHJlc3VsdCBmb3IgdGhlIGNyb3NzIG9mIHRoZSB0aHJlZSBmYWN0b3JzDQogICAgICAgIGFsbC5jcm9zc2VkIDwtIHNhcHBseShmYWN0b3IuY29sdW1uLmxldmVscywgZnVuY3Rpb24oYyl7DQogICAgICAgIGFsbC5jcm9zc2VkLnZlY3RvciA8LSBteS5kYXRhW215LmRhdGFbW2ZhY3Rvci5ibG9ja3NdXSA9PSBiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteS5kYXRhW1tmYWN0b3Iucm93c11dPT1yICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15LmRhdGFbW2ZhY3Rvci5jb2x1bW5zXV0gPT0gYywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15LmR2XQ0KICAgICAgICBhbGwuY3Jvc3NlZC52ZWN0b3IuY2xlYW4gPC0gYWxsLmNyb3NzZWQudmVjdG9yWyFpcy5uYShhbGwuY3Jvc3NlZC52ZWN0b3IpXQ0KICAgICAgICAgIGlmIChsZW5ndGgoYWxsLmNyb3NzZWQudmVjdG9yLmNsZWFuKSA+IDApe215LmZ1bmN0aW9uKGFsbC5jcm9zc2VkLnZlY3Rvci5jbGVhbil9DQogICAgICAgICAgICBlbHNlIHtOQV9yZWFsX30gIw0KICAgICAgICB9KQ0KICAgIA0KICAgICAgICAjIyAyLjUuIFNhdmUgdGhlIHJlc3VsdHMgb2YgZWFjaCBsaW5lIGluIGEgbmFtZWQgdmVjdG9yDQogICAgICAgIHJlc3VsdHMgW1trXV0gPC0gYyhncm91cD0gcm93LmxhYmVsLmJyLCBvdmVyYWxsID0gYnIuY3Jvc3NlZCwgc2V0TmFtZXMob2JqZWN0PWFsbC5jcm9zc2VkLCBmYWN0b3IuY29sdW1uLmxldmVscykpDQogICAgICAgIGsgPC0gaysxDQogICAgICAgIA0KICAgICAgfSAjIyNjbG9zZSB0aGUgbG9vcCBmb3IgdGhlIHJvd3MgZmFjdG9yDQogDQp9ICMjIyBjbG9zZSB0aGUgbG9vcCBmb3IgdGhlIGJsb2NrcyBmYWN0b3INCg0KDQogIA0KIyAzLiBXZSBhZ3JlZ2F0ZSB0aGUgcmVzdWx0cyBpbiBhIGRhdGFmcmFtZSBhbmQgcmV0dXJuIHRoZSBkYXRhZnJhbWUNCg0KcmVzdWx0cy5kZiA8LSBhcy5kYXRhLmZyYW1lKGRvLmNhbGwocmJpbmQsIHJlc3VsdHMpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCnJlc3VsdHMuZGZbLC0xXSA8LSBsYXBwbHkocmVzdWx0cy5kZlssLTFdLGFzLm51bWVyaWMpDQoNCnJldHVybihyZXN1bHRzLmRmKQ0KfQ0KDQoNCg0KYGBgDQoNCg0KDQoNCiMjIyAyLjIuMi4gSGVhdG1hcCBmb3IgTlBTIGluIGNhdGVnb3JpY2FsIHNjb3Jlcw0KDQpUbyBvYnRhaW4gdGhlIE5QUyBpbiBjYXRlZ29yaWNhbCB0ZXJtcywgd2UgYWxzbyBjcmVhdGVkIGEgc2ltcGxlIHJldXNhYmxlIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGVzZSBzY29yZXMgZnJvbSBhbnkgdmVjdG9yIG9mIG5wcyByYXcgc2NvcmVzLiAgDQoNCmBgYHtyfQ0KDQpmLm5wcy5jYXQgPC0gZnVuY3Rpb24gKG15Lm5wcy52ZWN0b3IsIG5hLnJtPVRSVUUpeyAjIG15Lm5wcy52ZWN0b3IgaXMgYSBudW1lcmljIHZlY3RvciBvZiByYXcgTlBTIHNjb3Jlcy4gSXQgY2FuIGNvbnRhaW4gTkFzDQogIA0KICB2ZWN0b3IuYyA8LSBteS5ucHMudmVjdG9yWyFpcy5uYShteS5ucHMudmVjdG9yKV0gIyB2ZWN0b3IgY2xlYW4gb2YgTkFzDQogIA0KICAjIGVycm9yIG1lc3NhZ2VzIGlmIHNhbXBsZSBzaXplID0gMCBvciBpZiB0aGVyZSBhcmUgc2NvcmVzIG91dCBvZiB0aGUgdHlwaWNhbCByYW5nZSBvZiBucHMgcmF3IHNjb3JlczoNCiAgaWYobGVuZ3RoKHZlY3Rvci5jKSA9PSAwKSB7c3RvcCgiZXJyb3I6IG5vIGF2YWlsYWJsZSBucHMgcmF3IHNjb3JlcyIpfSANCiAgaWYobGVuZ3RoKHZlY3Rvci5jW3ZlY3Rvci5jID4gMTBdKSA+IDApIHtzdG9wKCJlcnJvcjogc2NvcmVzIGhpZ2hlciB0aGFuIDEwIGluIHRoZSBucHMgcmF3IHNjb3JlcyIpfSANCiAgaWYobGVuZ3RoKHZlY3Rvci5jW3ZlY3Rvci5jIDwgMF0pID4gMCkge3N0b3AoImVycm9yOiBzY29yZXMgbG93ZXIgdGhhbiAwIGluIHRoZSBucHMgcmF3IHNjb3JlcyIpfSANCiAgDQogICN3YXJuaW5nIGlmIHNhbXBsZSBzaXplIGlzIGJlbG93IDcwDQogIGlmKGxlbmd0aCh2ZWN0b3IuYykgPCA3MCkge3dhcm5pbmcoImZldyBzY29yZXMgYXZhaWxhYmxlIGZvciBjYWxjdWxhdGlvbiAobjw3MCkiKX0gDQogIA0KICBuIDwtIGxlbmd0aCh2ZWN0b3IuYykgIyBzYW1wbGUgc2l6ZSAod2l0aG91dCBtaXNzaW5nIGRhdGEpDQogIHByb3AucHJvbW90ZXJzIDwtIChsZW5ndGgodmVjdG9yLmNbdmVjdG9yLmM+PTldKSkvbiAjIHByb3BvcnRpb24gb2YgcHJvbW90ZXJzDQogIHByb3AuZGV0cmFjdG9ycyA8LSAobGVuZ3RoKHZlY3Rvci5jW3ZlY3Rvci5jPD02XSkpL24gI3Byb3BvcnRpb24gb2YgZGV0cmFjdG9ycw0KICBucHMuY2F0IDwtIChwcm9wLnByb21vdGVycyAtIHByb3AuZGV0cmFjdG9ycykqMTAwICMgbnBzIHNjb3JlIGluIGNhdGVnb3JpY2FsIHRlcm1zDQogIA0KICByZXR1cm4gKG5wcy5jYXQpICNyZXR1cm5zIG5wcyBpbiBjYXRlZ29yaWNhbCB0ZXJtcw0KfQ0KDQojQ2hlY2sgaWYgaXQgaXMgd29ya2luZyBhcyBleHBlY3RlZA0KDQpmLm5wcy5jYXQoZGF0YSRucHMuc2NvcmUpDQp3aXRoKGRhdGEsIHByb3AudGFibGUodGFibGUobnBzLnNlZ21lbnQpKSkNCg0KYGBgDQoNClRoZSBmdW5jdGlvbiBpcyB3b3JraW5nIGNvcnJlY3RseSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBzY29yZSB3ZSBvYnRhaW4gaXMgZXF1YWwgdG8gc3Vic3RyYWN0aW5nIHRoZSBwZXJjZW50YWdlIG9mIHByb21vdGVycyBhbmQgZGV0cmFjdG9ycyB0aGF0IHdlIGNhbGN1bGF0ZWQgd2l0aCB0aGUgdGFibGUgZnVuY3Rpb24gb24gdGhlIG5wcy5zZWdtZW50IHZhcmlhYmxlLiANCg0KWWV0LCBiZWZvcmUgZ2V0dGluZyByZXN1bHRzLCB3ZSBjaGVjayBpZiBvdXIgc2FtcGxlIHNpemVzIGFyZSBhY2NlcHRhYmxlIGFuZCBub3QgcHJvbmUgdG8gc2FtcGxpbmcgYmlhc2VzDQoNCmBgYHtyfQ0KDQpucHMuY2F0LmhlYXRtYXAubiA8LSBmLmhlYXRtYXAuM2YgKA0KICBteS5kYXRhID0gZGF0YSwNCiAgbXkuZHYgPSAibnBzLnNjb3JlIiwNCiAgZmFjdG9yLmNvbHVtbnMgPSAibWV0aG9kIiwNCiAgZmFjdG9yLnJvd3MgPSAicmVhc29uIiwNCiAgZmFjdG9yLmJsb2NrcyA9ICJjb3VudHJ5IiwNCiAgbXkuZnVuY3Rpb24gPSBsZW5ndGgNCikNCmBgYA0KDQpBbGwgc2FtcGxlIHNpemVzIGFyZSBoaWdoIGFjcm9zcyBhbGwgY2VsbHMgKG4gPiAxNTApLCBzdWdnZXN0aW5nIGxpdHRsZSBwcm9iYWJpbGl0eSBvZiBzYW1wbGluZyBlcnJvci4gDQoNCk5vdyB3ZSBhcmUgcmVhZHkgdG8gY2FsY3VsYXRlIHRoZSBoZWF0bWFwIGZvciB0aGUgTlBTIGNhdGVnb3JpY2FsIHVzaW5nIHRoZSBmdW5jdGlvbnMgd2UgaGF2ZSBwcmVwYXJlZC4NCg0KDQpgYGB7cn0NCg0KbnBzLmNhdC5oZWF0bWFwIDwtIGYuaGVhdG1hcC4zZiAoDQogIG15LmRhdGEgPSBkYXRhLA0KICBteS5kdiA9ICJucHMuc2NvcmUiLA0KICBmYWN0b3IuY29sdW1ucyA9ICJtZXRob2QiLA0KICBmYWN0b3Iucm93cyA9ICJyZWFzb24iLA0KICBmYWN0b3IuYmxvY2tzID0gImNvdW50cnkiLA0KICBteS5mdW5jdGlvbiA9IGYubnBzLmNhdCAjQ2FsY3VsYXRpb24gb2YgbnBzIGNhdGVnb3JpY2FsIHZhbHVlcw0KKQ0KDQojIFdlIGV4cG9ydCBpdCB0byBleGNlbCB0byBhZGQgY29sb3JzIHdpdGggY29uZGl0aW9uYWwgZm9ybWF0dGluZw0Kd3JpdGUueGxzeCAobnBzLmNhdC5oZWF0bWFwLCAibnBzLmNhdC5oZWF0bWFwLnhsc3giLCBjb2xuYW1lcyA9IFRSVUUpIA0KDQpgYGANCg0KDQpBZnRlciBhcHBseWluZyBjb25kaXRpb25hbCBmb3JtYXR0aW5nIGluIGV4Y2VsIGJhc2VkIG9uIGNvbG9ycywgd2UgZ2V0IG91ciBoZWF0bWFwIHRvIGlkZW50aWZ5IGFyZWFzIGluIHdoaWNoIHByb2JhYmlsaXR5IHRvIHJlY29tbWVuZCBvdXIgY3VzdG9tZXIgc2VydmljZSBpcyB2ZXJ5IGxvdzogDQoNCg0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiSGVhdG1hcC5ucHMuY2F0LnBuZyIpDQpgYGANCg0KVGhlIG9ic2VydmF0aW9uIG9mIHRoZSBoZWF0bWFwIHN1Z2dlc3QgdXMgc2V2ZXJhbCBlZmZlY3RzOiANCiAgLSBDdXN0b21lcnMgaW4gRHJpbmtsYW5kIGhhdmUgbG93ZXIgcHJvYmFiaWxpdHkgdG8gcHJvbW90ZSB0aGFuIEVhdGxhbmQuIA0KICAtIEN1c3RvbWVycyBjb250YWN0aW5nIGZvciBjYW5jZWxsYXRpb25zIGhhdmUgbG93ZXIgcHJvYmFiaWxpdHkgdG8gcHJvbW90ZSB0aGFuIGN1c3RvbWVycyBjYWxsaW5nIGZvciBvdGhlciByZWFzb25zLiANCiAgLSBQaG9uZSBpcyB0aGUgY29udGFjdCBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlc3QgcHJvYmFiaWxpdHkgdG8gcHJvbW90ZSwgd2hpbGUgZW1haSBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIGxvd2VzdC4gDQogIC0gQ3VzdG9tZXJzIGNvbnRhY3RpbmcgdGhyb3VnaCBjaGF0IGZvciBpbmNvcnJlY3QgaXRlbSBkZWxpdmVyaWVzIGZlZWwgdmVyeSBsb3cgcHJvYmFiaWxpdHkgdG8gcHJvbW90ZS4gDQogIA0KWWV0LCBiZWZvcmUgd2UgcHJvY2VlZCB0byBmdXJ0aGVyIGludmVzdGlnYXRlIHRoZXNlIGVmZmVjdHMsIHdlIGNoZWNrIGlmIHdlIGZpbmQgc2ltaWxhciBwYXR0ZXJucyB3aXRoIG90aGVyIG1lYXN1cmVzLiBUaGUgTlBTIGNhdGVnb3JpY2FsLCBhbHRob3VnaCBoYXZlIHRoZSBhZHZhbnRhZ2Ugb2YgYmVpbmcgZmFtaWxpYXIgdG8gb3VyIHN0YWtlaG9sZGVycywgaGFzIHNldmVyYWwgbGltaXRhdGlvbnMsIHN1Y2ggYXMgcG90ZW50aWFsbHkgbGVhZGluZyB0byBjb25mdXNpb25zIGJlY2F1c2Ugb2YgdGhlIGluZm9ybWF0aW9uIGxvc3QgYnkgdGhlICUgc3Vic3RyYWN0aW9uIChpdCBpcyBiYXNlZCBvbiBzdWJzdHJhY3RpbmcgdGhlICUgb2YgcHJvbW90ZXJzLCB0aG9zZSB3aG8gc2NvcmUgYmV0d2VlbiA5IGFuZCAxMCwgdG8gdGhlICUgb2YgZGV0cmFjdG9ycywgdGhvc2Ugd2hvIHNjb3JlIGJldHdlZW4gMSBhbmQgNikuIFRvIGlsbHVzdHJhdGUgdGhpcyBsaW1pdGF0aW9uLCBhIGNhdGVnb3JpY2FsIE5QUyBzY29yZSBvZiAyMCBjYW4gY29tZSBmcm9tIGluZmluaXRlIGRpc3RyaWJ1dGlvbnMgb2YgTlBTIHJhdyBzY29yZXMsIGluY2x1ZGluZyBzb21lIGFzIGRpZmZlcmVudCBhcyA6IA0KLSBkaXN0cmlidXRpb24gYSkgNjAlIG9mIHByb21vdGVycyBhbmQgMjAlIG9mIGRldHJhY3RvcnMNCi0gZGlzdHJpYnV0aW9uIGIpIDIwJSBvZiBwcm9tb3RlcnMgYW5kIDAlIG9mIGRldHJhY3RvcnMuIA0KDQoNCg0KIyMjIDIuMi4zLiBIZWF0bWFwIGZvciBOUFMgaW4gcmF3IHNjb3Jlcw0KDQpUbyBtYWtlIHN1cmUgdGhlIE5QUyBjYXRlZ29yaWNhbCBpcyBub3QgbGVhZGluZyB1cyB0byBjb25mdXNpbmcgcGF0dGVybnMgYW5kIGFtYmlnw7xpdGllcywgd2UgY2hlY2sgaWYgd2Ugb2J0YWluIHNpbWlsYXIgcGF0dGVybnMgd2l0aCB0aGUgTlBTIHJhdyBzY29yZS4gV2UgYXBwbHkgb3VyIHByZW1haWQgaGVhdG1hcCBmb3JtdWxhIHRvIHRoZSBtZWFuIG9mIHRoZSBOUFMgcmF3IHNjb3Jlcw0KDQoNCmBgYHtyfQ0KDQpucHMucmF3LmhlYXRtYXAgPC0gZi5oZWF0bWFwLjNmICgNCiAgbXkuZGF0YSA9IGRhdGEsDQogIG15LmR2ID0gIm5wcy5zY29yZSIsDQogIGZhY3Rvci5jb2x1bW5zID0gIm1ldGhvZCIsDQogIGZhY3Rvci5yb3dzID0gInJlYXNvbiIsDQogIGZhY3Rvci5ibG9ja3MgPSAiY291bnRyeSIsDQogIG15LmZ1bmN0aW9uID0gbWVhbiAjIGNhbGN1bGF0aW9uIG9mIHRoZSBtZWFuIG9mIHRoZSBucHMgcmF3IHNjb3Jlcw0KKQ0KIyBFeHBvcnQgaXQgdG8gZXhjZWwgdG8gYWRkIGNvbG9ycyB3aXRoIGNvbmRpdGlvbmFsIGZvcm1hdHRpbmcNCndyaXRlLnhsc3ggKG5wcy5yYXcuaGVhdG1hcCwgIm5wcy5yYXcuaGVhdG1hcC54bHN4IiwgY29sbmFtZXMgPSBUUlVFKQ0KDQojIFdlIHZpc3VhbGl6ZSBib3RoIGhlYXRtYXBzIGFmdGVyIGFkZGluZyB0aGUgY29sb3JzIGluIGV4Y2VsOiANCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIkhlYXRtYXAubnBzMi5wbmciKQ0KDQpgYGANCg0KV2UgY2FuIHNlZSBhIHNpbWlsYXIgcGF0dGVybiBmb3IgYm90aCB3YXlzIHRvIGNhbGN1bGF0ZSB0aGUgcmVzdWx0cyBvZiB0aGUgTlBTLCBhcyB3ZSBkZXNjcmliZWQgaW4gdGhlIHNlY3Rpb24geC4gDQoNCllldCwgd2Ugc3RpbGwgaGF2ZSBkb3VidHMgYWJvdXQgdGhlIHF1YWxpdHkgb2YgdGhlIE5QUyBhcyBhIG1lYXN1cmUsIGluIHdoaWNoIGN1c3RvbWVycyBhc2sgZm9yIHRoZSBwcm9iYWJpbGl0eSB0byBwcm9tb3RlLiBUbyBmdXJ0aGVyIGdhaW4gY29uZmlkZW5jZSBvbiBpdCwgd2UgY2hlY2sgaXRzIGNvcnJlc3BvbmRlbmNlIHdpdGggdGhlIHNhdGlzZmFjdGlvbiBzY29yZXMuIA0KDQoNCg0KIyMjIDIuMi4yLiBIZWF0bWFwIGZvciBzYXRpc2ZhY3Rpb24gc2NvcmVzDQoNCkZvciBzYXRpc2ZhY3Rpb24gd2Ugb25seSBoYWQgc2NvcmVzIGZvciBvbmUgY291bnRyeSAoRWF0bGFuZCkgYW5kIGZvciAyIGNvbnRhY3QgbWV0aG9kcyAocGhvbmUgYW5kIGNoYXQpLiBIb3dldmVyLCB0aGVzZSBzY29yZXMgYXJlIGV4cGVjdGVkIHRvIGJlIGEgbW9yZSB2YWxpZCBtZWFzdXJlcyBvZiBzYXRpc2ZhY3Rpb24gdGhhbiB0aGUgTlBTIHNjb3Jlcywgc28gd2Ugd2lsbCB1c2UgdGhlbSB0byBzZWUgaWYgdGhleSBzaG93IGEgZGlmZmVyZW50IHBhdHRlcm4gdGhhbiB0aGUgTlBTIHNjb3Jlcy4gIA0KDQoNCkZvciB0aGF0LCB3ZSBhcHBseSB0aGUgZnVuY3Rpb24gd2UgY3JlYXRlZCBpbiB0aGUgcHJpb3Igc2VjdGlvbiB0byB0aGUgc2F0aXNmYWN0aW9uIHNjb3JlcywgYW5kIHdlIHNhdmUgdGhlIG9idGFpbmVkIGRhdGFmcmFtZSBpbnRvIGV4Y2VsIHRvIGZ1cnRoZXIgY2xlYW4gYW5kIGluY2x1ZGUgY29sb3JzIGluIHRoZSBoZWF0bWFwLiANCg0KDQpgYGB7cn0NCg0Kc2F0aXNmYWN0aW9uLmhlYXRtYXAgPC0gZi5oZWF0bWFwLjNmICgNCiAgbXkuZGF0YSA9IGRhdGEsDQogIG15LmR2ID0gInNhdGlzZmFjdGlvbiIsIA0KICBmYWN0b3IuY29sdW1ucyA9ICJtZXRob2QiLA0KICBmYWN0b3Iucm93cyA9ICJyZWFzb24iLA0KICBmYWN0b3IuYmxvY2tzID0gImNvdW50cnkiLA0KICBteS5mdW5jdGlvbiA9IG1lYW4gIyBjYWxjdWxhdGlvbiBvZiB0aGUgbWVhbiBmb3IgdGhlIHNhdGlzZmFjdGlvbiBzY29yZXMNCikNCg0KIyBFeHBvcnQgaXQgdG8gZXhjZWwgdG8gYWRkIGNvbG9ycyB3aXRoIGNvbmRpdGlvbmFsIGZvcm1hdHRpbmcNCndyaXRlLnhsc3ggKHNhdGlzZmFjdGlvbi5oZWF0bWFwLCAic2F0aXNmYWN0aW9uLmhlYXRtYXAueGxzeCIsIGNvbG5hbWVzID0gVFJVRSkNCg0KIyBXZSB2aXN1YWxpemUgYWxsIGhlYXRtYXBzIGFmdGVyIGFkZGluZyB0aGUgY29sb3JzIGluIGV4Y2VsOiANCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJIZWF0bWFwLnBuZyIpDQoNCg0KYGBgDQoNCldlIGNhbiBzZWUgYSBzaW1pbGFyIHBhdHRlcm4gZm9yIHNhdGlzZmFjdGlvbiBhbmQgYm90aCB3YXlzIHRvIGNhbGN1bGF0ZSB0aGUgcmVzdWx0cyBvZiB0aGUgTlBTLCBzdWdnZXN0aW5nIHRoYXQgb3VyIE5QUyBjYXRlZ29yaWNhbCBtZWFzdXJlIGluIHRoaXMgY29udGV4dCwgaXMgYSBnb29kIGluZGljYXRvciBvZiBzYXRpc2ZhY3Rpb24sIGFuZCB0aGF0IHdlIGNhbiBzaW1wbGlmeSBvdXIgY29udmVyc2F0aW9ucyB3aXRoIHN0YWtlaG9sZGVycyBieSBmb2N1c2luZyBvbiB0aGlzIG1lYXN1cmUuIA0KDQpZZXQsIHRvIG1ha2Ugc3VyZSBvdXIgc2lnaHQgaXMgbm90IHRyaWNraW5nIHVzLCB3ZSBhbHNvIGNoZWNrIGhvdyB0aGUgcmVzdWx0cyBhcmUgY29ycmVsYXRlZA0KDQoNCg0KIyMjMi4yLjQuIENvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBiZXR3ZWVuIE5QUyBtZWFzdXJlcyBhbmQgc2F0aXNmYWN0aW9uDQoNClRvIHNlZSB0aGUgc3RyZW5ndGggb2YgdGhlIGNvcnJlbGF0aW9ucyB3ZSB3aWxsIGNhbGN1bGF0ZSBQZWFyc29uIGNvcnJlbGF0aW9ucyBhY3Jvc3MgdGhlIHRocmVlIG1lYXN1cmVzIGZvciBlYWNoIGNlbGwgaW4gdGhlIGhlYXRtYXAuIEZvciB0aGF0LCB3ZSBmaXJzdCBtb3ZlIGFsbCB0aGUgY2VsbHMgb2YgdGhlIGhlYXRtYXBzIGRhdGFiYXNlcyBpbiBhIHNpbmdsZSBjb2x1bW4sIHRoZW4gYXBwbHkgdGhlIGNvcnJlbGF0aW9uLiANCg0KDQoNCmBgYHtyfQ0KI01vdmUgYWxsIGNlbGxzIGluIHRoZSBoZWF0bWFwcyB0byBhIHNpbmdsZSBjb2x1bW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCnNhdC5sb25nIDwtIHNhdGlzZmFjdGlvbi5oZWF0bWFwICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoLTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZXZhbHVhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAic2F0aXNmYWN0aW9uIikgIyByZXN1bHRzIG9mIHNhdGlzZmFjdGlvbiBoZWF0bWFwIGluIGEgc2luZ2xlIGNvbHVtbg0KDQpucHMucmF3LmxvbmcgPC0gbnBzLnJhdy5oZWF0bWFwICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoLTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZXZhbHVhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibnBzLnJhdyIpICAjIHJlc3VsdHMgb2YgbnBzIHJhdyBoZWF0bWFwIGluIGEgc2luZ2xlIGNvbHVtbg0KDQpucHMuY2F0LmxvbmcgPC0gbnBzLmNhdC5oZWF0bWFwICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoLTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZXZhbHVhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibnBzLmNhdCIpICAjIHJlc3VsdHMgb2YgbnBzIGNhdCBoZWF0bWFwIGluIGEgc2luZ2xlIGNvbHVtbg0KDQojTWVyZ2UgdGhlIGNvbHVtbnMgZm9yIGFsbCBtZWFzdXJzIGluIGEgc2luZ2xlIGRhdGFzZXQNCm1lcmdlZC5oZWF0bWFwcyA8LW1lcmdlKG5wcy5jYXQubG9uZywgKG1lcmdlKHNhdC5sb25nLCBucHMucmF3LmxvbmcsIGJ5PSBjKCJncm91cCIsICJldmFsdWF0aW9uIikpKSwgYnkgPWMgKCJncm91cCIsICJldmFsdWF0aW9uIikpDQoNCiNBcHBseSBQZWFyc29uIGNvcnJlbGF0aW9ucw0KY29yKG1lcmdlZC5oZWF0bWFwc1ssMzo1XSwgdXNlPSJjb21wbGV0ZS5vYnMiKSAjQ29ycmVsYXRpb24gbWF0cml4IGFjcm9zcyB0aGUgcmVzdWx0cyBmb3IgdGhlIHRocmVlIG1lYXN1cmVzDQoNCmBgYA0KDQpBbGwgbWVhc3VyZXMgYXBwZWFyIHRvIGJlIHN0cm9uZ2x5IGNvcnJlbGF0ZWQgKHIgPiAuOTApLCBnaXZpbmcgdXMgY29uZmlkZW5jZSBvbiB0aGUgTlBTIGNhdGVnb3JpY2FsIGJlaW5nIGEgZ29vZCBpbmRpY2F0b3Igb2Ygc2F0aXNmYWN0aW9uIGFuZCBwcm9iYWJpbGl0eSB0byBwcm9tb3RlLiBUaGlzIGlzIGEgZ29vZCBuZXcgYmVjYXVzZSBvdXIgc3Rha2Vob2xkZXJzIGFyZSBtb3JlIGZhbWlsaWFyIHdpdGggdGhpcyBtZWFzdXJlLCBhbmQgd2UgY2FuIHVzZSBpdHMgaGVhdG1hcCB0byBkaXNjdXNzIGVmZmVjdHMgd2l0aCB0aGVtLiANCg0KDQoNCg0KDQojIyAyLjMuIFN0YXRpc3RpY2FsIE1vZGVscyBhYm91dCBGYWN0b3JzIFJlbGF0ZWQgdG8gUHJvYmFiaWxpdHkgdG8gUHJvbW90ZS4gDQoNCldoaWxlIHRoZSB2aXN1YWxpemF0aW9uIG9mIHRoZSBoZWF0bWFwcyB3YXMgYWxyZWFkeSBpbmZvcm1pbmcgdXMgYWJvdXQgaW1wb3J0YW50IGFzcGVjdHMgaW4gd2hpY2ggY3VzdG9tZXIgc2VydmljZSBpcyBwZXJmb3JtaW5nIGxvd2VyLCB3ZSBmdXJ0aGVyIGV4cGxvcmUgdGhlc2UgcmVsYXRpb25zIHdpdGggc3RhdGlzdGljYWwgbW9kZWxzLg0KDQojIyMgMi4zLjEuIEJ1aWxkaW5nIGFuIE9yZGluYWwgbG9naXN0aWMgbW9kZWwuIA0KDQpXZSBjb25zaWRlciB0aGF0IGFuIG9yZGluYWwgbG9naXN0aWMgbW9kZWwgaXMgbW9yZSBhcHByb3ByaWF0ZSB0aGFuIGEgbGluZWFsIG1vZGVsIGJlY2F1c2Ugb3VyIHZhcmlhYmxlIHRvIHByZWRpY3QsIG5wcyBzY29yZXMsIGlzIGFuIG9yZGluYWwgdmFyaWFibGUgaW4gd2hpY2ggaXQgaXMgZGlmZmljdWx0IHRvIGFzc3VtZSBzaW1pbGFyIGRpc3RhbmNlcyBhY3Jvc3MgYWxsIG9mIHRoZSBzY29yZXMsIHNwZWNpYWxseSBkdWUgdG8gdGhlIGNlaWxpbmcgYW5kIGZsb29yIGVmZmVjdHMgdGhhdCB3ZSBpbmZlciBmcm9tIGl0cyBkaXN0cmlidXRpb24gKHNlZSBzZWN0aW9uIHgpLiANCg0KRnVydGhlcm1vcmUsIGEgbGluZWFyIG1vZGVsIHdvdWxkIG5vdCBmdWxmaWxsIHRoZSBhc3N1bXB0aW9uIGZvciB0aGUgbm9ybWFsaXR5IG9mIHJlc2lkdWFscywgYXMgd2UgY2FuIHNlZSBpbiB0aGUgUVEgcGxvdHMgYmVsb3csIGV2ZW4gd2hlbiB3ZSB1c2UgYSBzcXVhcmUgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHZhcmlhYmxlLiAgDQoNCmBgYHtyfQ0KI0NoZWNraW5nIG5vcm1hbGl0eSBhbmQgaG9tb2NlZGFzdGljeSBvZiByZXNpZHVhbHMgb2YgdGhlIGxpbmVhciBtb2RlbA0KbGluZWFyLm1vZGVsIDwtIGxtIChucHMuc2NvcmUgfiBtZXRob2QgKyByZWFzb24gKyBjb3VudHJ5LCBkYXRhID1kYXRhKQ0KcGFyKG1mcm93ID1jKDIsMikpDQpwbG90KGxpbmVhci5tb2RlbCkNCg0KI0NoZWNraW5nIG5vcm1hbGl0eSBhbmQgaG9tb2NlZGFzdGljeSBvZiByZXNpZHVhbHMgb2YgdGhlIGxpbmVhciBtb2RlbCB3aXRoIHRoZSBzcXVhcmVkIHRyYW5zZm9ybWF0aW9uDQpsaW5lYXIubW9kZWwuc3EgPC0gbG0gKChucHMuc2NvcmVeMikgfiBtZXRob2QgKyByZWFzb24gKyBjb3VudHJ5LCBkYXRhID1kYXRhKQ0KcGFyKG1mcm93ID1jKDIsMikpDQpwbG90KGxpbmVhci5tb2RlbC5zcSkNCg0KYGBgDQoNCg0KQXMgd2UgY2FuIHNlZSBpbiB0aGUgUS1RIHBsb3RzLCB0aGUgcmVzaWR1YWxzJyBkaXN0cmlidXRpb25zIGRpZmZlciBzdHJvbmdseSBmcm9tIHRoZSBoeXBvdGhldGljYWwgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gDQoNCkF0IHRoaXMgcG9pbnQsIHdlIGFsc28gY29uc2lkZXJlZCB0aGF0IGFuIG9yZGluYWwgbW9kZWwgd2FzIHByZWZlcmFibGUgdGhhbiBhIGJpbm9taWFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGJlY2F1c2UgaXQgd291bGQgcHJvdmlkZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZWZmZWN0cyBvZiB0aGUgZmFjdG9ycyBhY3Jvc3MgdGhlIHdob2xlIE5QUyBzY2FsZSwgcmF0aGVyIHRoYW4ganVzdCBvbiBhIHNpbmdsZSBOUFMgY2F0ZWdvcnkuICANCg0KSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB3ZSBhbHNvIGhhZCB0aGUgb3B0aW9uIHRvIHVzZSBhIA0KRm9yIGFsbCBvZiB0aGVzZSByZWFzb25zLCB3ZSBwcm9jZWVkIHdpdGggb3JkaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uLiANCg0KDQojIyMyLjMuMS4xLiBTZXR0aW5nIFJlZmVyZW5jZXMgYWNyb3NzIHRoZSBDYXRlZ29yaWVzIG9mIHRoZSBGYWN0b3JzDQoNCkp1c3QgdG8gbWFrZSB0aGUgaW50ZXJwcmV0YXRpb24gbW9yZSBpbnR1aXRpdmUsIHdlIHNldCB0aGUgbGV2ZWxzIG9mIG91ciBmYWN0b3JzIGluIGEgd2F5IHRoYXQgdGhlIGxldmVscyB3aXRoIGhpZ2hlciBucHMgc2NvcmVzIGFyZSBzcGVjaWZpZWQgZmlyc3QgKGZvbGxvd2luZyB0aGUgaGVhdG1hcCB3ZSBjYWxjdWxhdGVkIGVhcmxpZXIpLiBUaGF0IHdheSwgdGhlIGJlc3QgbGV2ZWxzIHdpbGwgYmUgdXNlZCBhcyByZWZlcmVuY2UgdG8gY29tcGFyZSB0aGUgZWZmZWN0IG9mIHRoZSBvdGhlcnMuIA0KDQoNCmBgYHtyfQ0KZGF0YSRtZXRob2QgPC0gZmFjdG9yKGRhdGEkbWV0aG9kLCBsZXZlbHMgPSBjKCJwaG9uZSIsICJ3ZWJfZm9ybSIsICJjaGF0IiwgImVtYWlsIikpDQpkYXRhJHJlYXNvbiA8LSBmYWN0b3IoZGF0YSRyZWFzb24sIGxldmVscyA9IGMoIm90aGVyIiwic3RhdHVzIiwgImRlbGF5IiwgImNhbmNlbGxhdGlvbiIsICJpbmNvcnJlY3RfaXRlbSIpKQ0KZGF0YSRjb3VudHJ5IDwtIGZhY3RvcihkYXRhJGNvdW50cnksIGxldmVscyA9IGMoIkVhdGxhbmQiLCAiRHJpbmtsYW5kIikpDQoNCmBgYA0KDQoNCg0KIyMjIDIuMy4xLjIuIENoZWNraW5nIHRoZSBjb2xsaW5lYXJpdHkgYXNzdW1wdGlvbg0KDQpXZSBjaGVjayB0aGUgY29sbGluZWFyaXR5IGFjcm9zcyBvdXIgY2F0ZWdvcmljYWwgZmFjdG9ycyB1c2luZyB2aXN1YWwgcmVwcmVzZW50YXRpb25zIGFuZCBWSUYgc2NvcmVzLg0KDQpgYGB7cn0NCiMgTW9zYWljIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnMgYmV0d2VlbiBmYWN0b3JzICh1c2luZyB0aGUgdmNkIHBhY2thZ2UpDQpkb3VibGVkZWNrZXIod2l0aChkYXRhLCB0YWJsZShjb3VudHJ5LCByZWFzb24sIG1ldGhvZCApKSkNCg0KIyBWSUYgY29lZmZpY2llbnRzLCBleHRyYWN0ZWQgZnJvbSB0aGUgbGluZWFyIG1vZGVsIHdlIGNhbGN1bGF0ZWQgaW4gc2VjdGlvbiB4ICh1c2luZyB0aGUgY2FyIHBhY2thZ2UpDQp2aWYobGluZWFyLm1vZGVsKQ0KDQpgYGANCg0KVGhlIFZJRiBhbmFseXNpcyBpbmRpY2F0ZXMgYW4gYWJzZW5jZSBvZiBtdWx0aWNvbGxpbmVhcml0eSBhbW9uZyBvdXIgcHJlZGljdG9ycy4gRm9yIGFsbCBmYWN0b3JzLCB3ZSB1c2VkIHRoZSB0aGUgR1ZJRl4oMS8oMipEZikpIHZhbHVlcywgdGhlIG1vc3QgYXBwcm9wcmlhdGUgbWV0cmljIGZvciBjb21wYXJpbmcgcHJlZGljdG9ycyB3aXRoIGRpZmZlcmVudCBudW1iZXJzIG9mIGNhdGVnb3JpZXPigJRhcmUgZXh0cmVtZWx5IGNsb3NlIHRvIDEuMC4NCg0KVGhpcyBmaW5kaW5nIGlzIGZ1cnRoZXIgc3VwcG9ydGVkIGJ5IHRoZSBtb3NhaWMgcGxvdCwgd2hpY2ggdmlzdWFsbHkgZGVtb25zdHJhdGVzIHRoYXQgdGhlIGZyZXF1ZW5jaWVzIG9mIG9uZSBmYWN0b3IgYXJlIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSBsZXZlbHMgb2YgdGhlIG90aGVyIGZhY3RvcnMsIHNob3dpbmcgbm8gc3Ryb25nIGludGVyZGVwZW5kZW5jaWVzDQoNCg0KDQojIyMjIyAyLjMuMS4zLiBDaGVja2luZyB0aGUgUHJvcG9ydGlvbmFsIG9kZHMgYXNzdW1wdGlvbg0KDQoNCkJlZm9yZSB0ZXN0aW5nIHRoaXMgYXNzdW1wdGlvbiB3ZSBjcmVhdGUgZHVtbXkgdmFyaWFibGVzIGZvciB0aGUgZmFjdG9ycyB3aXRoIG11bHRpcGxlIGNhdGVnb3JpZXMsIHNvIHdlIGNhbiBzZWUgdGhlIHJlc3VsdHMgb2YgdGhlIHByb3BvcnRpb25hbCBvZGRzIHNlcGFyYXRlbHkgZm9yIGVhY2ggb2YgdGhlIHZhcmlhYmxlcy4gDQoNCldlIGFsc28gbWFrZSBzdXJlIG91ciBkZXBlbmRlbnQgdmFyaWFibGUgaXMgc3BlY2lmaWVkIGFzIG9yZGluYWwNCg0KYGBge3J9DQoNCiMgRHVtbXkgdmFyaWFibGVzIGZvciBtZXRob2QsIHVzaW5nIHBob25lIGFzIHRoZSByZWZlcmVuY2UNCmRhdGEkY2hhdCA8LSBOQV9yZWFsXw0KZGF0YSRjaGF0W2RhdGEkbWV0aG9kID09ICJjaGF0Il0gPC0gMQ0KZGF0YSRjaGF0WyEoZGF0YSRtZXRob2QgPT0gImNoYXQiKV0gPC0gMA0KDQpkYXRhJGVtYWlsIDwtIE5BX3JlYWxfDQpkYXRhJGVtYWlsW2RhdGEkbWV0aG9kID09ICJlbWFpbCJdIDwtIDENCmRhdGEkZW1haWxbIShkYXRhJG1ldGhvZCA9PSAiZW1haWwiKV0gPC0gMA0KDQpkYXRhJHdlYl9mb3JtIDwtIE5BX3JlYWxfDQpkYXRhJHdlYl9mb3JtW2RhdGEkbWV0aG9kID09ICJ3ZWJfZm9ybSJdIDwtIDENCmRhdGEkd2ViX2Zvcm1bIShkYXRhJG1ldGhvZCA9PSAid2ViX2Zvcm0iKV0gPC0gMA0KDQojIER1bW15IHZhcmlhYmxlcyBmb3IgcmVhc29uLCB1c2luZyBzdGF0dXMgYXMgdGhlIHJlZmVyZW5jZQ0KZGF0YSRkZWxheSA8LSBOQV9yZWFsXw0KZGF0YSRkZWxheVtkYXRhJHJlYXNvbiA9PSAiZGVsYXkiXSA8LSAxDQpkYXRhJGRlbGF5WyEoZGF0YSRyZWFzb24gPT0gImRlbGF5IildIDwtIDANCg0KZGF0YSRjYW5jZWxsYXRpb24gPC0gTkFfcmVhbF8NCmRhdGEkY2FuY2VsbGF0aW9uW2RhdGEkcmVhc29uID09ICJjYW5jZWxsYXRpb24iXSA8LSAxDQpkYXRhJGNhbmNlbGxhdGlvblshKGRhdGEkcmVhc29uID09ICJjYW5jZWxsYXRpb24iKV0gPC0gMA0KDQpkYXRhJGluY29ycmVjdF9pdGVtPC0gTkFfcmVhbF8NCmRhdGEkaW5jb3JyZWN0X2l0ZW1bZGF0YSRyZWFzb24gPT0gImluY29ycmVjdF9pdGVtIl0gPC0gMQ0KZGF0YSRpbmNvcnJlY3RfaXRlbVshKGRhdGEkcmVhc29uID09ICJpbmNvcnJlY3RfaXRlbSIpXSA8LSAwDQoNCmRhdGEkc3RhdHVzIDwtIE5BX3JlYWxfDQpkYXRhJHN0YXR1c1tkYXRhJHJlYXNvbiA9PSAic3RhdHVzIl0gPC0gMQ0KZGF0YSRzdGF0dXNbIShkYXRhJHJlYXNvbiA9PSAic3RhdHVzIildIDwtIDANCg0KDQojIFJlZ2lzdGVyIHRoZSBucHMgc2NvcmUgYXMgYW4gb3JkaW5hbCBmYWN0b3INCmRhdGEkbnBzLnNjb3JlIDwtIGZhY3RvciggZGF0YSRucHMuc2NvcmUsIGxldmVscyA9IDA6MTAsIG9yZGVyZWQgPSBUUlVFKQ0KDQpgYGANCg0KDQoNCldlIGNoZWNrIHRoZSBwcm9wb3J0aW9uYWwgb2RkcyBhc3N1bXB0aW9uIHdpdGggdGhlIG9yZGluYWwgcGFja2FnZQ0KDQpgYGB7cn0NCg0KDQojU3BlY2lmeSB0aGUgYmFzaWMgb3JkaW5hbCBtb2RlbCB3aXRoIHRoZSBtYWluIGVmZmVjdHMNCm9yZGluYWwubW9kZWwgPC0gY2xtKG5wcy5zY29yZSB+IHdlYl9mb3JtICsgY2hhdCArIGVtYWlsICsgZGVsYXkgKyBzdGF0dXMgKyBjYW5jZWxsYXRpb24gKyBpbmNvcnJlY3RfaXRlbSAgKyBjb3VudHJ5LCBkYXRhID0gZGF0YSkNCnN1bW1hcnkgKG9yZGluYWwubW9kZWwpDQojQ2hlY2sgdGhlIHByb3BvcnRpb25hbCBvZGRzIGFzc3VtcHRpb24NCm5vbWluYWxfdGVzdChvcmRpbmFsLm1vZGVsKQ0KDQoNCg0KYGBgDQoNClRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiB3ZWJfZm9ybSwgYWxsIHRoZSBmYWN0b3JzIGZvciBjb250YWN0IG1ldGhvZHMgYW5kIGNvbnRhY3QgcmVhc29ucyB2aW9sYXRlIHRoZSBwcm9wb3J0aW9uYWwgb2RkcyBhc3N1bXB0aW9uLCB0aGF0IGlzLCB0aGF0IHRoZWlyIGVmZmVjdHMgb24gYSBjdXN0b21lcidzIE5QUyBzY29yZSBpcyBub3QgY29uc2lzdGVudCBhY3Jvc3MgdGhlIGVudGlyZSAwLTEwIHNjYWxlLg0KDQoNCkF0dGVuZGluZyB0byB0aGlzLCB3ZSB3aWxsIGNoZWNrIGlmIHRoaXMgaXNzdWUgY2FuIGJlIGNvcnJlY3RlZCBieSBpbmNvcnBvcmF0aW5nIGludGVyYWN0aW9ucyBiZXR3ZWVuIHRoZSBtZXRob2QgYW5kIHJlYXNvbiBmYWN0b3JzIHRoYXQgd2UgaGF2ZSBpZGVudGlmaWVkIGluIG91ciBoZWF0bWFwLiBXZSBjYW4gYWxzbyBzZWUgaWYgc2ltcGxpZnlpbmcgdGhlIG1vZGVsIGNhbiBoZWxwDQoNCklmIG5vdCwgd2Ugd2lsbCB0cnkgYSBtb3JlIGZsZXhpYmxlIG1vZGVsIHRoYXQgY2FsY3VsYXRlIHNlcGFyYXRlIHNldCBvZiBjb2VmZmljaWVudHMgZm9yIG1ldGhvZCBhbmQgcmVhc29uIGF0IGVhY2ggdGhyZXNob2xkIG9mIHRoZSBucHMgc2NvcmVzLg0KDQoNCiMjIyMjIDIuMy4xLjMgQWRkcmVzc2luZyBOb24tUHJvcG9ydGlvbmFsIE9kZHMgYnkgSW5jb3Jwb3J0YXRpbmcgSW50ZXJhY3Rpb25zIGludG8gdGhlIE1vZGVsDQoNCkluIHRoZSBoZWF0bWFwcyB3ZSBvYnNlcnZlZCBhbiBpbXBvcnRhbnQgaW50ZXJhY3Rpb246IGN1c3RvbWVycyBjb250YWN0aW5nIHRocm91Z2ggY2hhdCBmb3IgaW5jb3JyZWN0IGl0ZW0gZGVsaXZlcmllcyBmZWVsIHZlcnkgbG93IHByb2JhYmlsaXR5IHRvIHByb21vdGUgb3VyIGN1c3RvbWVyIHNlcnZpY2UuIA0KDQpUaGUgaW5jb3Jwb3JhdGlvbiBvZiB0aGlzIGludGVyYWN0aW9uIHRvIHRoZSBtb2RlbCBub3Qgb25seSBjYW4gYmUgaW5mb3JtYXRpdmUsIGl0IGNvdWxkIGVsaW1pbmF0ZSB0aGUgbm9uLXByb3BvcnRpb25hbCBvZGRzIHRoYXQgd2UgaGFkIGlkZW50aWZpZWQgZm9yIHRoZSBtZXRob2QgYW5kIHRoZSByZWFzb24gZmFjdG9ycy4gDQoNCldlIGNyZWF0ZSB0aGlzIG1vZGVsIHVzaW5nIHRoZSBvcmRpbmFsIHBhY2thZ2U6IA0KDQpgYGB7cn0NCg0KIyBQcmlvciBzdGVwOiBjcmVhdGlvbiBvZiBhIGRpY290b21vdXMgdmFyaWFibGUgdG8gYWNjb3VudCBmb3IgdGhlIGludGVyYWN0aW9uOiB3aGV0aGVyIGNhc2VzIGFyZSBkdWUgdG8gaW5jb3JyZWN0IGl0ZW0gY2FzZXMgYW5kIGF0dGVuZGVkIGJ5IGNoYXQNCg0KZGF0YSRjaGF0X2luY29ycmVjdF9pdGVtIDwtIE5BX3JlYWxfDQpkYXRhJGNoYXRfaW5jb3JyZWN0X2l0ZW1bZGF0YSRtZXRob2QgPT0gImNoYXQiICYgZGF0YSRyZWFzb24gPT0gImluY29ycmVjdF9pdGVtIl0gPC0gMQ0KZGF0YSRjaGF0X2luY29ycmVjdF9pdGVtWyEoZGF0YSRtZXRob2QgPT0gImNoYXQiICYgZGF0YSRyZWFzb24gPT0gImluY29ycmVjdF9pdGVtIildIDwtIDANCg0Kd2l0aChkYXRhLCh0YWJsZShjaGF0X2luY29ycmVjdF9pdGVtLCBtZXRob2QsIHJlYXNvbikpKSAjIHRvIGNoZWNrIHRoZSB2YXJpYWJsZSBoYXMgYmVlbiBjcmVhdGVkIGFzIGV4cGVjdGVkDQoNCiMgV2UgYnVpbGQgdGhlIG9yZGluYWwgbG9naXN0aWMgbW9kZWwgd2l0aCB0aGUgb3JkaW5hbCBwYWNrYWdlOiANCm9yZGluYWwubW9kZWwuaW50IDwtICBjbG0obnBzLnNjb3JlIH4gd2ViX2Zvcm0gKyBjaGF0ICsgZW1haWwgKyBkZWxheSArIHN0YXR1cyArIGNhbmNlbGxhdGlvbiArIGluY29ycmVjdF9pdGVtICArIGNvdW50cnkgKyBjaGF0X2luY29ycmVjdF9pdGVtLCAgZGF0YSA9IGRhdGEpIA0KDQpzdW1tYXJ5KG9yZGluYWwubW9kZWwuaW50KQ0KDQphbm92YShvcmRpbmFsLm1vZGVsLCBvcmRpbmFsLm1vZGVsLmludCkNCg0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQpgYGANCg0KVGhlIG1vZGVsIHNob3dzIHRoYXQgdGhlIGludGVyYWN0aW9uIGlzIHNpZ25pZmljYW50LiBGdXJ0aGVybW9yZSwgdGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCBpbmRpY2F0ZSBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQgaW4gbW9kZWwgZml0IGZvciB0aGUgbW9yZSBjb21wbGV4IG1vZGVsIHRoYXQgaW5jbHVkZXMgdGhlIGNoYXRfaW5jb3JyZWN0X2l0ZW0gaW50ZXJhY3Rpb24uIFRoaXMgaXMgc3VwcG9ydGVkIGJ5IGEgc2lnbmlmaWNhbnQgY2hpLXNxdWFyZWQgdGVzdCAocCA8IDIuMmUtMTYpIGFuZCBhIHNpZ25pZmljYW50bHkgbG93ZXIgQUlDICgxNjEsNTc4LjQpIGNvbXBhcmVkIHRvIHRoZSBzaW1wbGVyIG1vZGVsIHdpdGggb25seSBtYWluIGVmZmVjdHMgKEFJQyA9IDE2MSw4MzAuNykuIA0KDQoNCk5vdywgbGV0J3MgY2hlY2sgaWYgdGhlIHByb3BvcnRpb25hbCBvZGRzIGFzc3VtcHRpb24gb2YgdGhlIG5ldyBtb2RlbCBpcyBmdWxmaWxsZWQNCg0KDQpgYGB7cn0NCg0KI0NoZWNrIHRoZSBwcm9wb3J0aW9uYWwgb2RkcyBhc3N1bXB0aW9uDQpub21pbmFsX3Rlc3Qob3JkaW5hbC5tb2RlbC5pbnQpDQpgYGANCg0KVGhlIGluY2x1c2lvbiBvZiB0aGUgaW50ZXJhY3Rpb24gaGFzIGltcHJvdmVkIHRoZSBmdWxmaWxsbWVudCBvZiB0aGUgcHJvcG9ydGlvbmFsIG9kZHMgZm9yIHNvbWUgZmFjdG9ycy4gSXQgaXMgbm93IGZ1bGZpbGxlZCBmb3IgdGhlIGNvbnRhY3QgcmVhc29uIG9mIGRlbGF5LiBTaW5jZSB0aGlzIGZhY3RvciBpcyBub3QgYXNzb2NpYXRlZCB3aXRoIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBjb21wYXJpc29uIHdpdGggdGhlIG90aGVyIGNvbnRhY3QgcmVhc29ucyB1c2VkIGFzIGEgcmVmZXJlbmNlLCB3ZSBjb3VsZCB0YWtlIGl0IGF3YXkgZnJvbSB0aGUgbW9kZWwuIA0KDQpIb3dldmVyLCBub24gcHJvcG9ydGlvbmFsIG9kZHMgYXJlIHN0aWxsIHByb2JsZW1hdGljIGZvciBvdGhlciBjb250YWN0IHJlYXNvbnMgYW5kIGNvbnRhY3QgbWV0aG9kcy4gVG8gZnVydGhlciBleHBsb3JlIGhvdyB0aGVzZSBub24gcHJvcG9ydGlvbmFsIG9kZHMgYXJlIGFjdGluZyB3ZSB2aXN1YWxpemUgaml0dGVyIHBsb3RzIHdpdGggdGhlIGdncGxvdDIgcGFja2FnZTogDQoNCmBgYHtyfQ0KDQojUmV1c2FibGUgZnVuY3Rpb24gZm9yIGNvbWJpbmluZyBqaXR0ZXIgcGxvdHMgYW5kIGJveCBwbG90czogDQoNCmYubnBzLmppdHRlcl9ib3ggPC0gZnVuY3Rpb24gKG15LmRhdGEsIG15LmZhY3RvciwgbXkuZHYpew0KICBnZ3Bsb3QobXkuZGF0YSwgYWVzKHggPSBmYWN0b3IoLmRhdGFbW215LmZhY3Rvcl1dKSwgeSA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKC5kYXRhW1tteS5kdl1dKSkpKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC40LCBhbHBoYSA9IDAuMSwgY29sb3IgPSAiYmx1ZSIpICsgIyBBZGp1c3RlZCB3aWR0aCBhbmQgYWxwaGEgZm9yIGNsYXJpdHkNCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKSArICMgQWRkIHRyYW5zcGFyZW50IGJveHBsb3RzIHRvIHNob3cgbWVkaWFuL3F1YXJ0aWxlcw0KICBsYWJzKHRpdGxlID0gcGFzdGUoIk5QUyBTY29yZSBieSIsIG15LmZhY3RvciksDQogICAgICAgeSA9IG15LmR2LA0KICAgICAgIHggPSBteS5mYWN0b3IpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IDA6MTApICsgIyBFbnN1cmUgYWxsIE5QUyBzY29yZXMgYXJlIHRpY2tzDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiTm8iLCAiWWVzIikpDQp9DQoNCiNBcHBsaWNhdGlvbiBvZiB0aGUgZnVuY3Rpb24gdG8gZWFjaCBmYWN0b3INCg0KIyBGb3IgcGVvcGxlIGNvbnRhY3RpbmcgdGhyb3VnaCBjaGF0OiAgICAgIA0KZi5ucHMuaml0dGVyX2JveChkYXRhLCAiY2hhdCIsICJucHMuc2NvcmUiKQ0KDQojIEZvciBwZW9wbGUgY29udGFjdGluZyB0aHJvdWdoIGVtYWlsDQpmLm5wcy5qaXR0ZXJfYm94KGRhdGEsICJlbWFpbCIsICJucHMuc2NvcmUiKQ0KDQojIEZvciBwZW9wbGUgY29udGFjdGluZyBmb3Igc3RhdHVzIGlzc3Vlcw0KZi5ucHMuaml0dGVyX2JveChkYXRhLCAic3RhdHVzIiwgIm5wcy5zY29yZSIpDQoNCiMgRm9yIHBlb3BsZSBjb250YWN0aW5nIGZvciBjYW5jZWxsYXRpb24gaXNzdWVzDQpmLm5wcy5qaXR0ZXJfYm94KGRhdGEsICJjYW5jZWxsYXRpb24iLCAibnBzLnNjb3JlIikNCg0KIyBGb3IgcGVvcGxlIGNvbnRhY3RpbmcgZm9yIGluY29ycmVjdCBpdGVtIGlzc3Vlcw0KZi5ucHMuaml0dGVyX2JveChkYXRhLCAiaW5jb3JyZWN0X2l0ZW0iLCAibnBzLnNjb3JlIikNCg0KIyBGb3IgcGVvcGxlIGNvbnRhY3RpbmcgZm9yIGluY29ycmVjdCBpdGVtIGlzc3VlcyB2aWEgY2hhdA0KZi5ucHMuaml0dGVyX2JveChkYXRhLCAiY2hhdF9pbmNvcnJlY3RfaXRlbSIsICJucHMuc2NvcmUiKQ0KYGBgDQpUaGUgZ3JhcGhzIGlsbHVzdHJhdGUgdGhhdCB0aGUgaW5mbHVlbmNlIG9mIHRoZSBmYWN0b3JzIHRoYXQgZG8gbm90IGZ1bGZpbGwgdGhlIHByb3BvcnRpb25hbCBvZGRzIGFzc3VtcHRpb24gYXBwZWFycyB0byBiZSBzdHJvbmdlciBpbiB0aGUgbG93ZXIgaW50ZXJ2YWxzIG9mIHRoZSBOUFMgc2NhbGUgKGkuZS4sIGRpZmZlcmVuY2VzIG1vcmUgbWFya2VkIGluIHRoZSBmaXJzdCBxdWFydGlsZSBhbmQgYmVsb3cgaXQpLiBUaGF0IGlzLCB0aGUgZmFjdG9ycyBhcHBlYXIgdG8gaGF2ZSBlZmZlY3QgaW4gdGhlIGNyZWF0aW9uIG9mIGRldHJhY3RvcnMuICANCg0KVG8gYmV0dGVyIGFkZHJlc3MgdGhlc2Ugbm9uLXByb3BvcnRpb25hbCBvZGRzLCB3ZSB3aWxsIHRyeSB0byBhY2NvdW50IGZvciB0aGVtIGluIG91ciBtb2RlbC4gDQoNCiMjIyMjIDIuMy4xLjQuIEFkZHJlc3NpbmcgTm9uLVByb3BvcnRpb25hbCBPZGRzIGJ5IEFjY291bnRpbmcgZm9yIFRoZW0gaW4gdGhlIE1vZGVscw0KDQpXZSBidWlsZCBhIG1vZGVsIHRoYXQgYXNzdW1lcyB0aGF0IHRoZSBpbmZsdWVuY2Ugb2YgZmFjdG9ycyB3aXRoIG5vbi1wcm9wb3J0aW9uYWwgb2RkcyBjYW4gaGF2ZSBkaWZmZXJlbnQgY29lZmZpY2llbnRzIGZvciBlYWNoIGludGVydmFsIG9mIHRoZSBOUFMgc2NvcmVzLCB1c2luZyB0aGUgVkdBTSBwYWNrYWdlDQoNCldlIGFsc28gdGFrZSBhd2F5IGZyb20gdGhlIG1vZGVsIHRoZSBmYWN0b3Igb2YgY29udGFjdGluZyBmb3IgZGVsYXlzLCBzaW5jZSB0aGlzIGZhY3RvciBmdWxmaWxsZWQgcHJvcG9ydGlvbmFsIG9kZHMgYW5kIHdhcyBub3QgYXNzb2NpYXRlZCB3aXRoIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIHRoZSBucHMgc2NvcmVzLiANCg0KYGBge3J9DQoNCg0KIyBNb2RlbCB3aGVyZSBTT01FIHByZWRpY3RvcnMgYXJlIGFzc3VtZWQgdG8gYmUgbm90IHByb3BvcnRpb25hbA0Kb3JkaW5hbC5ub25fcHJvcG9ydGlvbmFsLm1vZGVsLmludCA8LSB2Z2xtKA0KICBucHMuc2NvcmUgfiB3ZWJfZm9ybSArIGNoYXQgKyBlbWFpbCArIHN0YXR1cyArIGNhbmNlbGxhdGlvbiArIGluY29ycmVjdF9pdGVtICArIGNvdW50cnkgKyBjaGF0X2luY29ycmVjdF9pdGVtLA0KICBmYW1pbHkgPSBjdW11bGF0aXZlKGxpbmsgPSBsb2dpdGxpbmssIHBhcmFsbGVsID0gfiBjb3VudHJ5ICsgd2ViX2Zvcm0pLA0KICBkYXRhID0gZGF0YQ0KKQ0KDQpzdW1tYXJ5KG9yZGluYWwubm9uX3Byb3BvcnRpb25hbC5tb2RlbC5pbnQpIA0KYGBgDQpUaGUgbW9kZWwgZGlkIG5vdCBmaXQgY29ycmVjdGx5LCBhbmQgaXQgaXMgc2hvd2luZyB3YXJuaW5ncyB0aGF0IGl0IGlzIG92ZXItcHJlZGljdGluZyBzb21lIG91dGNvbWVzLCBsZWFkaW5nIHRvIHVuc3RhYmxlIGNhbGN1bGF0aW9ucy4NCg0KV2Ugd2lsbCB0cnkgYSBzaW1wbGUgbW9kZWwgaW4gd2hpY2ggd2Ugb25seSBhc3N1bWUgYXMgbm9uLXByb3BvcnRpb25hbCBpbiB0aGUgb25lcyBpbiB3aGljaCB0aGlzIGxhY2sgb2Ygb2RkcyBwcm9wb3J0aW9uYWxpdHkgaXMgaGlnaGVyOiB0aGUgY29udGFjdCByZWFzb24gb2YgaW5jb3JyZWN0IGl0ZW1zLCBhbmQgdGhlIGludGVyYWN0aW9uIGZhY3RvciBvZiBpbmNvcnJlY3QgaXRlbXMgYXR0ZW5kZWQgYnkgY2hhdDogDQoNCg0KDQpgYGB7cn0NCiMgTW9kZWwgd2hlcmUgZmV3ZXIgcHJlZGljdG9ycyBhcmUgYXNzdW1lZCB0byBiZSBub3QgcHJvcG9ydGlvbmFsDQpvcmRpbmFsLm5vbl9wcm9wb3J0aW9uYWwubW9kZWwuaW50MiA8LSB2Z2xtKA0KICBucHMuc2NvcmUgfiB3ZWJfZm9ybSArIGNoYXQgKyBlbWFpbCArIHN0YXR1cyArIGNhbmNlbGxhdGlvbiArIGluY29ycmVjdF9pdGVtICArIGNvdW50cnkgKyBjaGF0X2luY29ycmVjdF9pdGVtLA0KICBmYW1pbHkgPSBjdW11bGF0aXZlKGxpbmsgPSBsb2dpdGxpbmssIHBhcmFsbGVsID0gfiBjb3VudHJ5ICsgd2ViX2Zvcm0gKyBjaGF0ICsgZW1haWwgKyBzdGF0dXMgKyBjYW5jZWxsYXRpb24pLA0KICBkYXRhID0gZGF0YQ0KKQ0KDQpzdW1tYXJ5KG9yZGluYWwubm9uX3Byb3BvcnRpb25hbC5tb2RlbC5pbnQyKQ0KDQoNCmBgYA0KDQpUaGlzIG1vZGVsIHN0aWxsIGRpZCBub3QgZml0IGNvcnJlY3RseSwgYW5kIGl0IGlzIHNob3dpbmcgd2FybmluZ3MgdGhhdCBpdCBpcyBsZWFkaW5nIHRvIHVuc3RhYmxlIGNhbGN1bGF0aW9ucy4gV2Ugd2lsbCB0cnkgYW4gZXZlbiBtb3JlIHNpbXBsZSBtb2RlbCBpbiB3aGljaCB3ZSBvbmx5IGFzc3VtZSBhcyBub24tcHJvcG9ydGlvbmFsIHRoZSBpbnRlcmFjdGlvbiBmYWN0b3Igb2YgaW5jb3JyZWN0IGl0ZW1zIGF0dGVuZGVkIGJ5IGNoYXQ6IA0KDQoNCmBgYHtyfQ0KIyBNb2RlbCB3aGVyZSBvbmx5IHRoZSBpbnRlcmFjdGlvbiBpcyBhc3N1bWVkIHRvIGJlIG5vdCBwcm9wb3J0aW9uYWwNCm9yZGluYWwubm9uX3Byb3BvcnRpb25hbC5tb2RlbC5pbnQzIDwtIHZnbG0oDQogIG5wcy5zY29yZSB+IHdlYl9mb3JtICsgY2hhdCArIGVtYWlsICsgc3RhdHVzICsgY2FuY2VsbGF0aW9uICsgaW5jb3JyZWN0X2l0ZW0gICsgY291bnRyeSArIGNoYXRfaW5jb3JyZWN0X2l0ZW0sDQogIGZhbWlseSA9IGN1bXVsYXRpdmUobGluayA9IGxvZ2l0bGluaywgcGFyYWxsZWwgPSB+IGNvdW50cnkgKyB3ZWJfZm9ybSArIGNoYXQgKyBlbWFpbCArIHN0YXR1cyArIGNhbmNlbGxhdGlvbiArIGluY29ycmVjdF9pdGVtKSwNCiAgZGF0YSA9IGRhdGENCikNCg0Kc3VtbWFyeShvcmRpbmFsLm5vbl9wcm9wb3J0aW9uYWwubW9kZWwuaW50MykNCmBgYA0KDQpUaGlzIG1vZGVsIHN0aWxsIGRpZCBub3QgZml0IGNvcnJlY3RseSwgaW5kaWNhdGluZyB0aGF0IG91ciBkYXRhIGRvZXMgbm90IHdvcmsgd2VsbCB3aXRoIG5vbiBwcm9wb3J0aW9uYWwgYXNzdW1wdGlvbnMuIA0KDQoNCiMjIyAyLjMuMS41LiBVbnNvbHZhYmxlIGxpbWl0YXRpb25zIG9mIHRoZSBvcmRpbmFsIG1vZGVsIGR1ZSBub24tcHJvcG9ydGlvbmFsIG9kZHMNCg0KQXR0ZW5kaW5nIHRvIHRoZSBkaWZmaWN1bHRpZXMgdG8gYXNzdW1lIG5vbi1wcm9wb3J0aW9uYWwgb2RkcyBpbiBvdXIgbW9kZWwsIHdlIGNvbnNpZGVyZWQgdGhhdCB0aGUgcHJpb3Igb3JkaW5hbCBtb2RlbCBpcyB0aGUgYmVzdCBtb2RlbCB3ZSBjYW4gZXN0aW1hdGUgd2l0aCBvcmRpbmFsIHJlZ3Jlc3Npb24sIHdob3NlIHN1bW1hcnkgd2UgcmVjYWxsIGJlbG93OiANCg0KYGBge3J9DQpzdW1tYXJ5IChvcmRpbmFsLm1vZGVsLmludCkNCmBgYA0KDQoNCllldCwgd2Ugc2hvdWxkIGludGVycHJldCB0aGlzIG1vZGVsIHdpdGggY2F1dGlvbiwgY29uc2lkZXJpbmcgdGhhdCB0aGUgZWZmZWN0cyByZXByZXNlbnRlZCBieSB0aGUgY29lZmZpY2llbnRzIGFyZSB2YXJpYWJsZSBkZXBlbmRpbmcgb24gdGhlIGludGVydmFscyBvZiB0aGUgbnBzIHNjb3Jlcy4gU3BlY2lmaWNhbGx5LCB0aGV5IGFwcGVhciB0byBiZSBtb3JlIGludGVuc2UgaW4gdGhlIGxvd2VyIGludGVydmFscyBvZiB0aGUgTlBTLiBBdHRlbmRpbmcgdG8gdGhpcywgd2UgcHJvdmlkZSBhIGNvbXBsZW1lbnRhcnkgbW9kZWwgaW4gd2hpY2ggd2UgZXN0aW1hdGUgdGhlIHByb2JhYmlsaXR5IG9mIG9idGFpbmluZyBzY29yZXMgaW4gdGhlc2UgbG93ZXJzIGludGVydmFscywgc3BlY2lmaWNhbGx5IHRoZSBzY29yZXMgZnJvbSAwIHRvIDYsIHdoaWNoIGFyZSBnZW5lcmFsbHkgY29uc2lkZXJlZCBhcyBkZXRyYWN0b3JzLiANCg0KDQojIyMgMi4zLjIuIEJpbm9taWFsIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwgdG8gUHJlZGljdCBEZXRyYWN0b3JzDQoNCkEgYmlub21pYWwgbW9kZWwgdG8gcHJlZGljdCBkZXRyYWN0b3JzIG5vdCBvbmx5IGlzIGEgd2F5IHRvIGJ5cGFzc3MgdGhlIHByb3BvcnRpb25hbCBvZGRzIGFzc3VtcHRpb24gb2YgdGhlIG9yZGluYWwgbW9kZWwsICB3ZSBhbHNvIGV4cGVjdCB0byBzaG93IHRoZSBtb3N0IGltcG9ydGFudCBlZmZlY3RzIGluIG91ciBkYXRhLCBzaW5jZSB3ZSBoYXZlIG9ic2VydmVkIHRoYXQgdGhlIGVmZmVjdHMgb2YgdGhlIGZhY3RvcnMgd2FzIGVzcGVjaWFsbHkgcmVmbGVjdGVkIGluIHRoZSBzY29yZXMgb2YgdGhlIG5wcyBiZWxvdyA1IChzZWUgZ3JhcGhzKS4gDQoNCg0KYGBge3J9DQoNCiNDcmVhdGUgYSBkaWNvdG9tb3VzIHZhcmlhYmxlIGZvciB1c2VycyB3aG8gYXJlIGRldHJhY3RvcnMNCg0KZGF0YSRkZXRyYWN0b3IgPC0gTkENCg0KZGF0YSRkZXRyYWN0b3IgW2RhdGEkbnBzLnNlZ21lbnQgPT0gImRldHJhY3RvciJdIDwtIDENCmRhdGEkZGV0cmFjdG9yIFshKGRhdGEkbnBzLnNlZ21lbnQgPT0gImRldHJhY3RvciIpXSA8LSAwDQoNCg0KIyBGaXQgdGhlIGJpbm9taWFsIG1vZGVsDQpiaW5vbWlhbC5tb2RlbCA8LSBnbG0oZGV0cmFjdG9yIH4gd2ViX2Zvcm0gKyBjaGF0ICsgZW1haWwgKyBzdGF0dXMgKyBjYW5jZWxsYXRpb24gKyBpbmNvcnJlY3RfaXRlbSAgKyBjb3VudHJ5ICsgY2hhdF9pbmNvcnJlY3RfaXRlbSwgZGF0YSA9IGRhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCnN1bW1hcnkoYmlub21pYWwubW9kZWwpDQoNCmBgYA0KDQpUaGUgcmVzdWx0cyBhcmUgY29oZXJlbnQgd2l0aCB0aGUgb3JkaW5hbCBtb2RlbCwgYnV0IG1vcmUgc3RhYmxlIGFuZCBlc3BlY2lmaWNhbGx5IGZvY3VzZWQgb24gdGhlIHByZXNlbmNlIG9mIGRldHJhY3RvcnMuIA0KDQpUaGUgY29udGFjdCByZWFzb24gc3RhdHVzIGlzIG5vdCBzaG93aW5nIGFueSBzaWduaWZpY2FudCBlZmZlY3QsIHNvIHdlIHRlc3QgaWYgd2UgY2FuIHNpbXBsaWZ5IHRoZSBtb2RlbCBieSByZW1vdmluZyBpdDogDQoNCmBgYHtyfQ0KIyBGaXQgdGhlIGJpbm9taWFsIG1vZGVsIGNsZWFuZWQgZm9yIGlycmVsZXZhbnQgZmFjdG9ycw0KYmlub21pYWwubW9kZWwuYyA8LSBnbG0oZGV0cmFjdG9yIH4gd2ViX2Zvcm0gKyBjaGF0ICsgZW1haWwgKyBjYW5jZWxsYXRpb24gKyBpbmNvcnJlY3RfaXRlbSAgKyBjb3VudHJ5ICsgY2hhdF9pbmNvcnJlY3RfaXRlbSwgZGF0YSA9IGRhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCnN1bW1hcnkoYmlub21pYWwubW9kZWwuYykNCg0KYW5vdmEoYmlub21pYWwubW9kZWwuYywgYmlub21pYWwubW9kZWwsIHRlc3QgPSJDaGlzcSIpDQoNCg0KYGBgDQpCYXNlZCBvbiBvdXIgYW5hbHlzaXMsIHRoZSBzdGF0dXMgdmFyaWFibGUgZG9lcyBub3QgaGF2ZSBhIHNpZ25pZmljYW50IGltcGFjdCBvbiBwcmVkaWN0aW5nIHdoZXRoZXIgYSBjdXN0b21lciB3aWxsIGJlIGEgZGV0cmFjdG9yLldlIHdpbGwgdGhlcmVmb3JlIHJlbW92ZSBpdCB0byBzaW1wbGlmeSB0aGUgbW9kZWwsIGFsbG93aW5nIHVzIHRvIGZvY3VzIG9uIHRoZSBtb3N0IHJlbGV2YW50IGFuZCBhY3Rpb25hYmxlIGRyaXZlcnMuDQoNCg0KDQpVc2luZyB0aGlzIG1vZGVsLCB3ZSBjYWxjdWxhdGUgdGhlIG9kZHMgcmF0aW9zIG9mIHRoZSBjb2VmZmljaWVudHMgYW5kIHRoZWlyIGNvbmZpZGVuY2UgaW50ZXJ2YWxzOiANCg0KYGBge3J9DQojQ2FsY3VsYXRlIHRoZSBvZGRzIHJhdGlvcw0KcmF0aW9zIDwtIGV4cChjb2VmKGJpbm9taWFsLm1vZGVsLmMpKQ0KcmF0aW9zDQoNCiNDYWxjdWxhdGUgdGhlaXIgY29uZmlkZW5jZSBpbnRlcnZhbHMNCmNpLnJhdGlvcyA8LSBleHAoY29uZmludChiaW5vbWlhbC5tb2RlbC5jKSkNCmNpLnJhdGlvcw0KDQoNCmBgYA0KQmFzZWQgb24gb3VyIGFuYWx5c2lzLCB3ZSBjYW4gbm93IGNsZWFybHkgaWRlbnRpZnkgdGhlIHByaW1hcnkgZHJpdmVycyB0aGF0IGluY3JlYXNlIGEgY3VzdG9tZXIncyBvZGRzIG9mIGJlY29taW5nIGEgZGV0cmFjdG9yLiBFeGFtcGxlcyBvZiBrZXkgZmluZGluZ3MgdG8gZGlzY3VzcyB3aXRoIHN0YWtlaG9sZGVycyBhcmU6DQoNCi0gQW1vbmcgdGhlIGRpZmZlcmVudCBjb250YWN0IGNoYW5uZWxzLCBlbWFpbCBpcyB0aGUgbW9zdCBzaWduaWZpY2FudCBpbmRpdmlkdWFsIGRyaXZlci4gSXQgaW5jcmVhc2VzIHRoZSBvZGRzIG9mIGEgY3VzdG9tZXIgYmVjb21pbmcgYSBkZXRyYWN0b3IgYnkgNzIlICg5NSUgQ0k6IDYyJSwgODMlKSBjb21wYXJlZCB0byBjb250YWN0aW5nIGJ5IHRlbGVwaG9uZS4NCg0KLSBBbiBpbXBvcnRhbnQgZHJpdmVyIG9mIGN1c3RvbWVyIGRpc3NhdGlzZmFjdGlvbiBpcyB0aGUgc3luZXJneSBiZXR3ZWVuIGNoYXQgYW5kIGFuIGluY29ycmVjdCBpdGVtLiBUaGlzIGNvbWJpbmF0aW9uIGFkZHMgYW4gZXh0cmEgMTgwJSBpbmNyZWFzZSB0byB0aGUgb2RkcyBvZiBhIGN1c3RvbWVyIGJlY29taW5nIGEgZGV0cmFjdG9yICg5NSUgQ0k6IDE1MiUsIDIxMCUpLCBvbiB0b3Agb2YgdGhlIGluZGl2aWR1YWwgZWZmZWN0cyBvZiBlYWNoIG9mIHRoZXNlIHR3byBmYWN0b3JzLg0KDQpXZSBjYW4gYWxzbyBvYnRhaW4gdGhlIHJlbGF0aXZlIHJpc2ssIHdoaWNoIGlzIG1vcmUgaW50dWl0aXZlIHRoYW4gdGhlIG9kZHMgcmF0aW8NCg0KYGBge3J9DQojV2UgY3JlYXRlIGEgZm9ybXVsYSB0byBvYnRhaW4gdGhlIHBlcmNlbnRhZ2UgY2hhbmdlLCByZWxhdGl2ZSBwcm9iYWJpbGl0aWVzLCBhbmQgb2RkcyByYXRpb3MNCg0KZi5wZXJjX2NoYW5nZS5iaW5vbWlhbCA8LSBmdW5jdGlvbihteS5tb2RlbCl7DQogIA0KICAjIE1ha2luZyBzdXJlIHRoZSBtb2RlbCBpcyBnbG0gYmlub21pYWwNCiAgaWYgKCFpbmhlcml0cyhteS5tb2RlbCwgImdsbSIpIHx8IG15Lm1vZGVsJGZhbWlseSRmYW1pbHkgIT0gImJpbm9taWFsIikgew0KICAgIHN0b3AoIlRoZSBmdW5jdGlvbiByZXF1aXJlcyBhIGJpbm9taWFsIG1vZGVsLiIpDQogIH0NCiAgDQogICMgMS4gT2J0YWluaW5nIHRoZSBjb2VmZmljaWVudHMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogIGNvZWZzIDwtIGNvZWYobXkubW9kZWwpDQogIGNpIDwtIGNvbmZpbnQobXkubW9kZWwpDQogIA0KICAjIDIuIEV4cG9uZW50aWF0aW9uIG9mIHRoZSBjb2VmZmljaWVudHMgYW5kIENJcyB0byBvYnRhaW4gb2RkcyByYXRpb3MNCiAgb3IgPC0gZXhwKGNvZWZzKQ0KICBvcl9jaSA8LSBleHAoY2kpDQogIA0KICAjIDMuIENhbGN1bGF0aW5nIHRoZSBiYXNlIHByb2JhYmlsaXR5IGZyb20gdGhlIGludGVyY2VwdA0KICBwMCA8LSBvclsxXSAvICgxICsgb3JbMV0pDQogIA0KICAjIDQuIE9idGFpbmluZyByZWxhdGl2ZSBwcm9iYWJpbGl0aWVzIGZyb20gcmlzayByYXRpb3MgYW5kIGJhc2UgcHJvYmFiaWxpdHkNCiAgcnAgPC0gb3IgLyAoKDEgLSBwMCkgKyAocDAgKiBvcikpDQogIA0KICAjIDUuIEFwbHlpbmcgdGhlIHNhbWUgZm9ybXVsYSB0byBvYnRhaW4gdGhlIGxpbWl0cyBvZiB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSByZWxhdGl2ZSBwcm9iYWJpbGl0aWVzDQogIHJwX2NpX2xvdyA8LSBvcl9jaVssIDFdIC8gKCgxIC0gcDApICsgKHAwICogb3JfY2lbLCAxXSkpDQogIHJwX2NpX2hpZ2ggPC0gb3JfY2lbLCAyXSAvICgoMSAtIHAwKSArIChwMCAqIG9yX2NpWywgMl0pKQ0KICANCiAgIyA2LiBPYnRhaW5pbmcgdGhlIHByb2JhYmlsaXR5IGNoYW5nZXMgYW5kIHRoZWlyIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogIHBjaCA8LSAocnAgLTEpDQogIHBjaF9jaV9sb3cgPC0gKHJwX2NpX2xvdyAtMSkNCiAgcGNoX2NpX2hpZ2ggPC0gKHJwX2NpX2hpZ2ggLTEpDQogIA0KICAjNy4gT2J0YWluaW5nIHRoZSBhYnNvbHV0ZSBjaGFuZ2VzDQogIHAuYWIuY2ggPC0gcDAqcnAgLSBwMA0KICANCiAgIyA2LiBDb21iaW5pbmcgdGhlIHJlc3VsdHMgaW4gYSBkYXRhZnJhbWUNCiAgcmVzdWx0cyA8LSBkYXRhLmZyYW1lKA0KICAgIFRlcm0gPSBuYW1lcyhycCksDQogICAgUGVyY2VudGFnZV9DaGFuZ2UgPSBhcy5udW1lcmljKHBjaCksDQogICAgUC5DaF9DSV9Mb3dlciA9IGFzLm51bWVyaWMocGNoX2NpX2xvdyksDQogICAgUC5DaF9DSV9VcHBlciA9IGFzLm51bWVyaWMocGNoX2NpX2hpZ2gpLA0KICAgIE9kZHNfUmF0aW8gPSBhcy5udW1lcmljKG9yKSwNCiAgICBSZWxhdGl2ZV9Qcm9iYWJpbGl0eSA9IGFzLm51bWVyaWMocnApLA0KICAgIFJQX0NJX0xvd2VyID0gYXMubnVtZXJpYyhycF9jaV9sb3cpLA0KICAgIFJQX0NJX1VwcGVyID0gYXMubnVtZXJpYyhycF9jaV9oaWdoKQ0KDQogICkNCiAgDQogICMgNy4gRWxpbWluYXRlIHRoZSBpbnRlcmNlcHQgbGluZQ0KICByZXN1bHRzLnJlbC5wcm9wIDwtIHJlc3VsdHNbLTEsIF0NCiAgDQogICMgOC4gQXJyYW5nZSByZXN1bHRzIGZyb20gZ3JlYXRlciBwZXJjZW50YWdlIGNoYW5nZSB0byBsb3dlcg0KICByZXN1bHRzLnJlbC5wcm9wIDwtIGFycmFuZ2UocmVzdWx0cy5yZWwucHJvcCwgZGVzYyhSZWxhdGl2ZV9Qcm9iYWJpbGl0eSkpDQogIA0KICAjIDkuIFJldHVybiB0aGUgcmVzdWx0cw0KICByZXR1cm4ocmVzdWx0cy5yZWwucHJvcCkNCn0NCg0KDQojQXBwbGljYXRpb24gb2YgdGhlIGZ1bmN0aW9uIHRvIG91ciBiaW5vbWlhbCBtb2RlbA0KcGVyYy5jaGFuZ2UuZGYgPC0gZi5wZXJjX2NoYW5nZS5iaW5vbWlhbCAoYmlub21pYWwubW9kZWwuYykNCnBlcmMuY2hhbmdlLmRmDQoNCmBgYA0KDQoNCg0KV2UgY2FuIGdyYXBoaWNhbGx5IHJlcHJlc2VudCB0aGVzZSByZXN1bHRzOiANCg0KDQpgYGB7cn0NCiMgV2UgcHJvdmlkZSBsYWJlbHMgZm9yIGVhY2ggZmFjdG9yIGluIHRoZSByZXN1bHRzIGRhdGFmcmFtZTogDQoNCnBlcmMuY2hhbmdlLmRmJEZhY3Rvci5MYWJlbCA8LSBOQV9jaGFyYWN0ZXJfDQpwZXJjLmNoYW5nZS5kZiRGYWN0b3IuTGFiZWwgW3BlcmMuY2hhbmdlLmRmJFRlcm0gPT0gImNoYXRfaW5jb3JyZWN0X2l0ZW0iXSA8LSAiSW5jb3JyZWN0IGl0ZW0gaXNzdWVzIGF0dGVuZGVkIHZpYSBjaGF0Ig0KcGVyYy5jaGFuZ2UuZGYkRmFjdG9yLkxhYmVsIFtwZXJjLmNoYW5nZS5kZiRUZXJtID09ICJlbWFpbCJdIDwtICJBdHRlbmRlZCB2aWEgZW1haWwiDQpwZXJjLmNoYW5nZS5kZiRGYWN0b3IuTGFiZWwgW3BlcmMuY2hhbmdlLmRmJFRlcm0gPT0gImNoYXQiXSA8LSAiQXR0ZW5kZWQgdmlhIGNoYXQiDQpwZXJjLmNoYW5nZS5kZiRGYWN0b3IuTGFiZWwgW3BlcmMuY2hhbmdlLmRmJFRlcm0gPT0gIndlYl9mb3JtIl0gPC0gIkF0dGVuZGVkIHZpYSB3ZWIgZm9ybSINCnBlcmMuY2hhbmdlLmRmJEZhY3Rvci5MYWJlbCBbcGVyYy5jaGFuZ2UuZGYkVGVybSA9PSAiY291bnRyeURyaW5rbGFuZCJdIDwtICJBdHRlbnRpb24gaW4gdGhlIGNvdW50cnkgRHJpbmtsYW5kIg0KcGVyYy5jaGFuZ2UuZGYkRmFjdG9yLkxhYmVsIFtwZXJjLmNoYW5nZS5kZiRUZXJtID09ICJjYW5jZWxsYXRpb24iXSA8LSAiQ3VzdG9tZXJzIGhhZCBhIGNhbmNlbGxhdGlvbiBpc3N1ZXMiDQpwZXJjLmNoYW5nZS5kZiRGYWN0b3IuTGFiZWwgW3BlcmMuY2hhbmdlLmRmJFRlcm0gPT0gImluY29ycmVjdF9pdGVtIl0gPC0gIkN1c3RvbWVycyBoYWQgaW5jb3JyZWN0IGl0ZW1zIGlzc3VlcyINCnBlcmMuY2hhbmdlLmRmICMgdG8gY2hlY2sgdGhlIHJlc3VsdHMgYXJlIGFzIGV4cGVjdGVkDQoNCg0KDQoNCg0KIyBDcmVhdGUgdGhlIGRvdC1hbmQtd2hpc2tlciBwbG90DQpnZ3Bsb3QocGVyYy5jaGFuZ2UuZGYsIGFlcyh4ID0gUGVyY2VudGFnZV9DaGFuZ2UsIA0KICAgICAgICAgICAgICAgICAgICB5ID0gcmVvcmRlcihGYWN0b3IuTGFiZWwsIFBlcmNlbnRhZ2VfQ2hhbmdlKSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyZXkiKSArDQogIGdlb21fZXJyb3JiYXJoKGFlcyh4bWluID0gUC5DaF9DSV9Mb3dlciwgeG1heCA9IFAuQ2hfQ0lfVXBwZXIpLCBoZWlnaHQgPSAwLjIpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkltcGFjdCBvZiBDdXN0b21lciBTZXJ2aWNlIEZhY3RvcnMgaW4gRGV0cmFjdG9ycyBQcm9iYWJpbGl0eSIsDQogICAgeCA9ICIlIG9mIEluY3JlYXNlIGluIERldHJhY3RvcnMgUHJvYmFiaWxpdHkiLA0KICAgIHkgPSAiQ3VzdG9tZXIgU2VydmljZSBGYWN0b3JzIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpDQoNCmBgYA0KDQpTRUdVSVIgQVFVw406IA0KT3BjacOzbiBBKSBWZXIgc2kgcHVlZG8gcG9uZGVyYXIgbG9zIHJlc3VsdGFkb3MgZGUgbGEgZ3LDoWZpY2EgYW50ZXJpb3IgcG9yIGVsIG7Dum1lcm8gZGUgY2Fzb3MuIFRlbmdvIGR1ZGFzLCBwb3JxdWUgbGEgcHJvYmFiaWxpZGFkIGRlIHNlciB1biBkZXRyYWN0b3IgKGVsIGludGVyY2VwdG8pIGNhbWJpYSBlbiBmdW5jacOzbiBkZWwgbsO6bWVybyBkZSBjYXNvcyBhc29jaWFkb3MgYSBlc2UgaW50ZXJjZXB0by4gwr9vIG5vPyB5byBjcmVvIHF1ZSBzZXLDrWEgaW5kZXBlbmRpZW50ZS4uLnB1ZXMgYXN1bW8gcXVlIHNpIGhlIGRpY2hvIHF1ZSBhdW1lbnRhbiB1biA1JSByZXNwZWN0byBhIGxhIGJhc2UgaW5pY2lhbCwgc2kgc8OpIHF1ZSBlc2UgNSUgZGUgaW5jcmVtZW50byBzw7NsbyBvY3VycmUgZW4gZWwgMTAlIGRlIGxvcyBjYXNvcywgZW50b25jZXMgeWEgbG8gdGVuZ28sIGVzIHVuIGltcGFjdG8gZGUgMC41JSBkZSBpbmNyZW1lbnRvIGVuIGVsIHBvcmNlbnRhamUgaW5pY2lhbCBkZSBkZXRyYWN0b3Jlcy4gTG8gcXVlIHlhIG5vIHPDqSBlcyBzaSBhcGxpY2EgYSBsb3MgaW50ZXJ2YWxvcyBkZSBjb25maWFuemEuIA0KDQpPUENJw5NOIEIpIExvIGRlIGludGVudGFyIGNhbGN1bGFyIGxvcyB2YWxvcmVzIHByZWRpY2hvcyB5IHN1cyBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YS4uLiB0YW1iacOpbiBwb2Ryw61hIGN1YWRyYXIsIHB1ZXMgZXMgZGlzdGludG8gYSBjYWxjdWxhciBsb3MgZGVzY3JpcHRpdm9zIHlhIHF1ZSBsb3MgZGVzY3JpcHRpdm9zIHNlIHZlbiBhZmVjdGFkb3MgcG9yIGxhIHByZXBvbmRlcmFuY2lhIGRlIG90cm9zIGZhY3RvcmVzLiBEZSBoZWNobyBtZSBwYXJlY2UgbGEgc29sdWNpw7NuIG3DoXMgbGltcGlhLiBZIGVuIGxhIHF1ZSB0cmFiYWpvIGNvbiB2YWxvcmVzIHByZWRpY2hvcywgcXVlIHRpZW5lIGJhc3RhbnRlIHV0aWxpZGFkIHBhcmEgbHVlZ28NCg0KDQpWZXIgc2kgcHVlZG8gY2FsY3VsYXIgbG9zIGluY3JlbWVudG9zIGFic29sdXRvcyBlbiBlbCBwb3JjZW50YWplIGRlIGRldHJhY3RvcmVzIGFzb2NpYWRvcyBhIGNhZGEgZmFjdG9yLCB5IGVsIElDIGRlIGVzb3MgaW5jcmVtZW50b3MuICANCg0KDQpEaWZlcmVuY2lhcyBhYnNvbHV0YXMNCg0KDQpgYGB7cn0NCg0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgUGFzbyAxOiBEZWZpbmlyIGVsIGVzY2VuYXJpbyBkZSByZWZlcmVuY2lhIChsYSBsw61uZWEgYmFzZSkNCiMgICAgICAgICBFc3RvIGNvcnJlc3BvbmRlIGFsIGludGVyY2VwdG8gZGUgdHUgbW9kZWxvLg0KIyAgICAgICAgIERlYmVzIGluY2x1aXIgVE9EQVMgbGFzIHZhcmlhYmxlcyBkZSB0dSBtb2RlbG8sDQojICAgICAgICAgY29uIGxvcyB2YWxvcmVzIGRlIHJlZmVyZW5jaWEgKDAgcGFyYSBsYXMgZHVtbWllcywNCiMgICAgICAgICAiRWF0bGFuZCIgcGFyYSBlbCBwYcOtcyBlbiBlc3RlIGNhc28pLg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCnJlZmVyZW5jaWEgPC0gZGF0YS5mcmFtZSgNCiAgd2ViX2Zvcm0gPSAwLA0KICBjaGF0ID0gMCwNCiAgZW1haWwgPSAwLA0KICBjYW5jZWxsYXRpb24gPSAwLA0KICBpbmNvcnJlY3RfaXRlbSA9IDAsDQogIGNvdW50cnkgPSAiRWF0bGFuZCINCikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFBhc28gMjogRGVmaW5pciBsb3MgZXNjZW5hcmlvcyBxdWUgcXVpZXJlcyBjb21wYXJhciBjb24gbGEgbMOtbmVhIGJhc2UNCiMgICAgICAgICBDcmVhIHVuIGRhdGFmcmFtZSBjb24gdW5hIGZpbGEgcGFyYSBjYWRhIGVzY2VuYXJpby4NCiMgICAgICAgICBBc2Vnw7pyYXRlIGRlIHF1ZSBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgY29pbmNpZGFuDQojICAgICAgICAgZXhhY3RhbWVudGUgY29uIGxhcyB2YXJpYWJsZXMgZGUgdHUgbW9kZWxvLg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmVzY2VuYXJpb3MgPC0gZGF0YS5mcmFtZSgNCiAgIyBTY2VuYXJpbzogV2ViIEZvcm0NCiAgd2ViX2Zvcm0gPSAxLCBjaGF0ID0gMCwgZW1haWwgPSAwLCBjYW5jZWxsYXRpb24gPSAwLCBpbmNvcnJlY3RfaXRlbSA9IDAsIGNvdW50cnkgPSAiRWF0bGFuZCIsDQogIA0KICAjIFNjZW5hcmlvOiBDaGF0DQogIHdlYl9mb3JtID0gMCwgY2hhdCA9IDEsIGVtYWlsID0gMCwgY2FuY2VsbGF0aW9uID0gMCwgaW5jb3JyZWN0X2l0ZW0gPSAwLCBjb3VudHJ5ID0gIkVhdGxhbmQiLA0KICANCiAgIyBTY2VuYXJpbzogRW1haWwNCiAgd2ViX2Zvcm0gPSAwLCBjaGF0ID0gMCwgZW1haWwgPSAxLCBjYW5jZWxsYXRpb24gPSAwLCBpbmNvcnJlY3RfaXRlbSA9IDAsIGNvdW50cnkgPSAiRWF0bGFuZCIsDQogIA0KICAjIFNjZW5hcmlvOiBDYW5jZWxsYXRpb24NCiAgd2ViX2Zvcm0gPSAwLCBjaGF0ID0gMCwgZW1haWwgPSAwLCBjYW5jZWxsYXRpb24gPSAxLCBpbmNvcnJlY3RfaXRlbSA9IDAsIGNvdW50cnkgPSAiRWF0bGFuZCIsDQogIA0KICAjIFNjZW5hcmlvOiBJbmNvcnJlY3QgSXRlbQ0KICB3ZWJfZm9ybSA9IDAsIGNoYXQgPSAwLCBlbWFpbCA9IDAsIGNhbmNlbGxhdGlvbiA9IDAsIGluY29ycmVjdF9pdGVtID0gMSwgY291bnRyeSA9ICJFYXRsYW5kIiwNCiAgDQogICMgU2NlbmFyaW86IENvdW50cnkgaXMgRHJpbmtsYW5kDQogIHdlYl9mb3JtID0gMCwgY2hhdCA9IDAsIGVtYWlsID0gMCwgY2FuY2VsbGF0aW9uID0gMCwgaW5jb3JyZWN0X2l0ZW0gPSAwLCBjb3VudHJ5ID0gIkRyaW5rbGFuZCIsDQogIA0KICAjIFNjZW5hcmlvOiBJbnRlcmFjdGlvbiBDaGF0ICYgSW5jb3JyZWN0IEl0ZW0NCiAgd2ViX2Zvcm0gPSAwLCBjaGF0ID0gMSwgZW1haWwgPSAwLCBjYW5jZWxsYXRpb24gPSAwLCBpbmNvcnJlY3RfaXRlbSA9IDEsIGNvdW50cnkgPSAiRWF0bGFuZCINCikNCg0KIyBUcmFuc3BvbmVyIGVsIGRhdGFmcmFtZSBwYXJhIHF1ZSBjYWRhIGZpbGEgc2VhIHVuIGVzY2VuYXJpbw0KZXNjZW5hcmlvcyA8LSBhcy5kYXRhLmZyYW1lKHQoZXNjZW5hcmlvcykpDQpyb3duYW1lcyhlc2NlbmFyaW9zKSA8LSBOVUxMICMgTGltcGlhciBsb3Mgbm9tYnJlcyBkZSBsYXMgZmlsYXMNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIFBhc28gMzogUHJlZGVjaXIgbGFzIHByb2JhYmlsaWRhZGVzIHkgc3VzIGludGVydmFsb3MgZGUgY29uZmlhbnphDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBQcm9iYWJpbGlkYWQgZGUgbGEgbMOtbmVhIGJhc2UNCnByZWRfYmFzZSA8LSBwcmVkaWN0KGJpbm9taWFsLm1vZGVsLmMsIG5ld2RhdGEgPSByZWZlcmVuY2lhLCB0eXBlID0gInJlc3BvbnNlIikNCnByb2JfYmFzZV9wY3QgPC0gcHJlZF9iYXNlICogMTAwDQoNCiMgUHJvYmFiaWxpZGFkZXMgZGUgbG9zIGVzY2VuYXJpb3MNCnByZWRfZXNjZW5hcmlvcyA8LSBwcmVkaWN0KGJpbm9taWFsLm1vZGVsLmMsIG5ld2RhdGEgPSBlc2NlbmFyaW9zLCB0eXBlID0gInJlc3BvbnNlIikNCnByb2JfZXNjZW5hcmlvc19wY3QgPC0gcHJlZF9lc2NlbmFyaW9zICogMTAwDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBQYXNvIDQ6IENhbGN1bGFyIGxhIGRpZmVyZW5jaWEgYWJzb2x1dGEgeSBzdXMgaW50ZXJ2YWxvcyBkZSBjb25maWFuemENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpkaWZlcmVuY2lhX2Fic29sdXRhX3BwIDwtIHByb2JfZXNjZW5hcmlvc19wY3QgLSBwcm9iX2Jhc2VfcGN0DQoNCiMgTk9UQTogQ2FsY3VsYXIgZWwgaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBwYXJhIGxhIGRpZmVyZW5jaWEgYWJzb2x1dGEgZXMgY29tcGxlam8uDQojICAgICAgIFBhcmEgdHUgcHJvcMOzc2l0bywgZXMgc3VmaWNpZW50ZSBtb3N0cmFyIGVsIGludGVydmFsbyBwYXJhIGNhZGEgcHJvYmFiaWxpZGFkLA0KIyAgICAgICB5YSBxdWUgc2kgbm8gc2Ugc29sYXBhbiwgbGEgZGlmZXJlbmNpYSBlcyBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhLg0KIyAgICAgICBFbCBjw7NkaWdvIGFudGVyaW9yIGVyYSBkZW1hc2lhZG8gY29tcGxlam8geSBlcmEgbGEgZnVlbnRlIGRlIGxvcyBlcnJvcmVzLg0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgUGFzbyA1OiBVbmlyIHRvZG8gZW4gdW4gZGF0YWZyYW1lIGZpbmFsIHkgb3JkZW5hcg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgQ3JlYXIgZWwgZGF0YWZyYW1lIGRlIHJlc3VsdGFkb3MNCnJlc3VsdHNfZGYgPC0gZGF0YS5mcmFtZSgNCiAgRXNjZW5hcmlvID0gYygNCiAgICAiV2ViIEZvcm0iLCAiQ2hhdCIsICJFbWFpbCIsICJDYW5jZWxsYXRpb24iLCANCiAgICAiSW5jb3JyZWN0IEl0ZW0iLCAiQ291bnRyeTogRHJpbmtsYW5kIiwgIkNoYXQgJiBJbmNvcnJlY3QgSXRlbSINCiAgKSwNCiAgUHJvYmFiaWxpZGFkX0Jhc2UgPSBwcm9iX2Jhc2VfcGN0LA0KICBQcm9iYWJpbGlkYWRfRXNjZW5hcmlvID0gcHJvYl9lc2NlbmFyaW9zX3BjdCwNCiAgRGlmZXJlbmNpYV9BYnNvbHV0YSA9IGRpZmVyZW5jaWFfYWJzb2x1dGFfcHANCikNCg0KIyBPcmRlbmFyIGxvcyByZXN1bHRhZG9zIHBhcmEgdW5hIG1lam9yIHZpc3VhbGl6YWNpw7NuDQpyZXN1bHRzX2RmIDwtIGFycmFuZ2UocmVzdWx0c19kZiwgZGVzYyhEaWZlcmVuY2lhX0Fic29sdXRhKSkNCg0KIyBJbXByaW1pciBsYSB0YWJsYSBmaW5hbA0KcHJpbnQocmVzdWx0c19kZikNCg0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCmNvZWZwbG90KGJpbm9taWFsLm1vZGVsLmMsIGludGVyY2VwdCA9IEZBTFNFLCBleHAgPSBUUlVFLA0KICAgICAgICAgeWxhYj0gIkN1c3RvbWVyIFNlcnZpY2UgRmFjdG9ycyIsDQogICAgICAgICB4bGFiID0gIk9kZHMgUmF0aW8gb2YgQmVjb21pbmcgYSBEZXRyYWN0b3IgKHZzLiBCYXNlbGluZSkiKQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQojIDIuIE9idGVuZXIgbG9zIGNvZWZpY2llbnRlcyB5IHN1cyBDSXMgKGVuIGVzY2FsYSBkZSBsb2ctb2RkcykNCmNvZWZzIDwtIHN1bW1hcnkoYmlub21pYWwubW9kZWwuYykkY29lZmZpY2llbnRzDQpjaXMgPC0gY29uZmludChiaW5vbWlhbC5tb2RlbC5jKQ0KDQojIDMuIENyZWFyIHVuIGRhdGFmcmFtZSBjb24gbG9zIGRhdG9zDQpwbG90X2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgdGVybSA9IHJvd25hbWVzKGNvZWZzKSwNCiAgZXN0aW1hdGUgPSBjb2Vmc1ssICJFc3RpbWF0ZSJdLA0KICBjb25mX2xvdyA9IGNpc1ssIDFdLA0KICBjb25mX2hpZ2ggPSBjaXNbLCAyXQ0KKQ0KDQojIEVsaW1pbmFyIGVsIGludGVyY2VwdG8gZGVsIGRhdGFmcmFtZQ0KcGxvdF9kYXRhIDwtIHBsb3RfZGF0YVstMSxdDQoNCiMgNC4gRXhwb25lbnRpYXIgdG9kb3MgbG9zIHZhbG9yZXMgYSBsYSBlc2NhbGEgZGUgT2RkcyBSYXRpbw0KcGxvdF9kYXRhJGVzdGltYXRlIDwtIGV4cChwbG90X2RhdGEkZXN0aW1hdGUpDQpwbG90X2RhdGEkY29uZl9sb3cgPC0gZXhwKHBsb3RfZGF0YSRjb25mX2xvdykNCnBsb3RfZGF0YSRjb25mX2hpZ2ggPC0gZXhwKHBsb3RfZGF0YSRjb25mX2hpZ2gpDQoNCiMgNS4gQ3JlYXIgZWwgZ3LDoWZpY28gY29uIGdncGxvdDINCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gZXN0aW1hdGUsIHkgPSB0ZXJtKSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtaW4gPSBjb25mX2xvdywgeG1heCA9IGNvbmZfaGlnaCksIGhlaWdodCA9IDAuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucz0nbG9nMTAnKSArICMgT3BjaW9uYWw6IHVzYXIgZXNjYWxhIGxvZ2Fyw610bWljYSBwYXJhIGVsIGVqZSB4IHBhcmEgbWVqb3IgdmlzdWFsaXphY2nDs24NCiAgbGFicygNCiAgICB4ID0gIk9kZHMgUmF0aW8gKHZzLiBCYXNlbGluZSkiLA0KICAgIHkgPSAiQ3VzdG9tZXIgU2VydmljZSBGYWN0b3JzIiwNCiAgICB0aXRsZSA9ICJJbXBhY3RvIGRlIGxvcyBmYWN0b3JlcyBlbiBsYSBwcm9iYWJpbGlkYWQgZGUgc2VyIGRldHJhY3RvciINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQpTRUdVSVIgQVFVw40NCg0KLSBHcmFwaHM6IHJlbGF0aXZlIHJpc2sgKGNvbiB5IHNpbiByZWxhdGl2aXphciBwb3IgbnVtZXJvIGRlIGxsYW1hZGFzKSArIGJhcmdyYXBocyAoY29uIHkgc2luIHJlbGF0aXZpemFyIHBvciBudW1lcm8gZGUgbGxhbWFkYXMgLSB0aXBvIG1vc2FpYyBwbG90IHBlcm8gZW4gbHVnYXIgZGVsIG51bWVybyBkZSBwZXJzb25hcyBudW1lcm8gZGUgbGxhbWFkYXMpDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KIyMjIFNlbGVjdGlvbiBvZiB0aGUgc2FtcGxlIGZvciB0aGUgYW5hbHlzZXMgb2YgY29tcGxhaW50cw0KDQpBbHRob3VnaCB3ZSBpbml0aWFsbHkgc2ltdWxhdGVkIGNvbXBsYWludHMgZm9yIGFsbCBzdWJqZWN0cyBpbiB0aGUgZGF0YXNldCwgd2UgZGlkbid0IGhhdmUgdGhpcyBkYXRhIGF2YWlsYWJsZSBmb3IgYWxsIG91ciBjYXNlcy4gVGhpcyBkYXRhIGNhbWUgZnJvbSBmdXJ0aGVyIGNvZGluZyBmb3Igc3ViZ3JvdXBzIHdlIHdhbnRlZCB0byBkbyBmb2xsb3ctdXAgYW5hbHlzZXMgKHNlZSBTZWN0aW9uIFgpLiBCYXNlZCBvbiB0aGlzLCB3ZSdyZSBub3cgc2VsZWN0aW5nIGEgc3RyYXRpZmllZCBzYW1wbGUgb2YgMTAwIGNhc2VzIGZvciBlYWNoIG9mIHRoZSBhdmFpbGFibGUgY29udGFjdCBtZXRob2RzLCBmb3IgZWFjaCBvZiB0aGUgZm91ciBjb250YWN0IHJlYXNvbnMgb2YgaW50ZXJlc3QgKCJpbmNvcnJlY3RfaXRlbSIsICJkZWxheSIsICJzdGF0dXMiLCAiY2FuY2VsbGF0aW9uIiksIGFuZCBmb3IgZWFjaCBvZiB0aGUgdHdvIHJlbGV2YW50IGNvdW50cmllcy4gVGhpcyBlbnN1cmVzIGEgYmFsYW5jZWQgcmVwcmVzZW50YXRpb24gYWNyb3NzIGtleSBkaW1lbnNpb25zIGZvciBvdXIgc3BlY2lhbGl6ZWQgYW5hbHlzZXMuDQoNClNwZWNpZmljYWxseSwgZm9yIGVhY2ggbWV0aG9kLCByZWFzb24sIGFuZCBjb3VudHJ5IGNvbWJpbmF0aW9uLCB3ZSBhaW0gdG8gb2J0YWluIGEgc2FtcGxlIG9mIDEwMCBzdWJqZWN0cyBhbmQgc3RvcmUgdGhlIHJlc3VsdHMgaW4gYSBuZXcgZGF0YXNldC4NCg0KYGBge3J9DQojIEZ1bmNpw7NuIHBhcmEgb2J0ZW5lciBsYXMgbcOpdHJpY2FzIGRlIGltcGFjdG8gZGUgdW4gbW9kZWxvIGJpbm9taWFsDQoNCmYubWV0cmljcy5iaW5vbWlhbCA8LSBmdW5jdGlvbihteS5tb2RlbCl7DQogIA0KICBpZiAoIWluaGVyaXRzKG15Lm1vZGVsLCAiZ2xtIikgfHwgbXkubW9kZWwkZmFtaWx5JGZhbWlseSAhPSAiYmlub21pYWwiKSB7DQogICAgc3RvcCgiTGEgZnVuY2nDs24gcmVxdWllcmUgdW4gb2JqZXRvIGRlIG1vZGVsbyBnbG0gY29uIGZhbWlsaWEgJ2Jpbm9taWFsJy4iKQ0KICB9DQogIA0KICAjIDEuIENyZWFyIHVuIGRhdGFmcmFtZSBjb24gbG9zIGVzY2VuYXJpb3MgYSBjb21wYXJhcg0KICAjICAgIE5lY2VzaXRhcyBkZWZpbmlyIHRvZGFzIHR1cyB2YXJpYWJsZXMgZHVtbXkgeSBkZSBpbnRlcmFjY2nDs24NCiAgZXNjZW5hcmlvcyA8LSBkYXRhLmZyYW1lKA0KICAgIFRlcm0gPSBuYW1lcyhjb2VmKG15Lm1vZGVsKSksDQogICAgd2ViX2Zvcm0gPSBjKDAsIDEsIDAsIDAsIDAsIDAsIDApLA0KICAgIGNoYXQgPSBjKDAsIDAsIDEsIDAsIDAsIDAsIDApLA0KICAgIGVtYWlsID0gYygwLCAwLCAwLCAxLCAwLCAwLCAwKSwNCiAgICBjYW5jZWxsYXRpb24gPSBjKDAsIDAsIDAsIDAsIDEsIDAsIDApLA0KICAgIGluY29ycmVjdF9pdGVtID0gYygwLCAwLCAwLCAwLCAwLCAxLCAwKSwNCiAgICBjb3VudHJ5RHJpbmtsYW5kID0gYygwLCAwLCAwLCAwLCAwLCAwLCAxKQ0KICApDQogIA0KICAjIEVsaW1pbmFyIGxhIGZpbGEgZGVsIGludGVyY2VwdG8gZGVsIGRhdGFmcmFtZQ0KICBlc2NlbmFyaW9zIDwtIGVzY2VuYXJpb3NbLTEsIF0NCiAgDQogICMgMi4gUHJlZGVjaXIgbGFzIHByb2JhYmlsaWRhZGVzIHBhcmEgY2FkYSBlc2NlbmFyaW8NCiAgIyAgICAndHlwZSA9ICJyZXNwb25zZSInIGRldnVlbHZlIGxhIHByb2JhYmlsaWRhZCBkaXJlY3RhbWVudGUNCiAgcHJvYmFiaWxpZGFkZXNfcHJlZGljaGFzIDwtIHByZWRpY3QobXkubW9kZWwsIG5ld2RhdGEgPSBlc2NlbmFyaW9zLCB0eXBlID0gInJlc3BvbnNlIikNCiAgDQogICMgMy4gQ2FsY3VsYXIgbGEgcHJvYmFiaWxpZGFkIGJhc2UNCiAgIyAgICBFc3RvIHNlIGhhY2UgcHJlZGljaWVuZG8gcGFyYSBlbCBlc2NlbmFyaW8gZGUgcmVmZXJlbmNpYSAodG9kbyBlbiAwKQ0KICBwcm9iX2Jhc2UgPC0gcHJlZGljdChteS5tb2RlbCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUod2ViX2Zvcm0gPSAwLCBjaGF0ID0gMCwgZW1haWwgPSAwLCBjYW5jZWxsYXRpb24gPSAwLCBpbmNvcnJlY3RfaXRlbSA9IDAsIGNvdW50cnlEcmlua2xhbmQgPSAwKSwgdHlwZSA9ICJyZXNwb25zZSIpDQogIA0KICAjIDQuIENhbGN1bGFyIGxhIGRpZmVyZW5jaWEgYWJzb2x1dGEgZW4gcHVudG9zIHBvcmNlbnR1YWxlcw0KICBkaWZlcmVuY2lhX2Fic29sdXRhX3BwIDwtIChwcm9iYWJpbGlkYWRlc19wcmVkaWNoYXMgLSBwcm9iX2Jhc2UpICogMTAwDQogIA0KICAjIDUuIENyZWFyIGVsIGRhdGFmcmFtZSBmaW5hbA0KICByZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogICAgVGVybSA9IGVzY2VuYXJpb3MkVGVybSwNCiAgICBQcm9iYWJpbGlkYWRfQmFzZV9wY3QgPSBwcm9iX2Jhc2UgKiAxMDAsDQogICAgTnVldmFfUHJvYmFiaWxpZGFkX3BjdCA9IHByb2JhYmlsaWRhZGVzX3ByZWRpY2hhcyAqIDEwMCwNCiAgICBEaWZlcmVuY2lhX0Fic29sdXRhX3BwID0gZGlmZXJlbmNpYV9hYnNvbHV0YV9wcA0KICApDQogIA0KICAjIDYuIE9yZGVuYXIgbG9zIHJlc3VsdGFkb3MNCiAgcmVzdWx0cyA8LSBkcGx5cjo6YXJyYW5nZShyZXN1bHRzLCBkZXNjKERpZmVyZW5jaWFfQWJzb2x1dGFfcHApKQ0KICANCiAgcmV0dXJuKHJlc3VsdHMpDQp9DQoNCmRpZmYucHJvcC5kZiA8LSBmLm1ldHJpY3MuYmlub21pYWwoYmlub21pYWwubW9kZWwuYykNCmRpZmYucHJvcC5kZg0KDQpgYGANCg0KDQpjb3JyZWxhY2nDs24geSBzY2F0dGVycGxvdA0KY8OzZGlnbyBwYXJhIGxhIG1hdHJpeiBkZSBOUFMNCmNvbiBhbGfDum4gYW7DoWxpc2lzIGFjb21wYcOxYW5kbzogdmVyIHNpIHVzbyBtb2RlbG8gbGluZWFsIG8gbG9nYXLDrXRtaWNvDQoNCg0KDQoNCg0KDQo=